Merge branch 'development' into LTS_development

This commit is contained in:
Thulinma 2018-12-19 15:47:22 +01:00
commit c926637aec
62 changed files with 9123 additions and 49744 deletions

View file

@ -469,22 +469,19 @@ add_executable(MistOutHTTP
src/output/output_http.cpp src/output/output_http.cpp
src/output/output_http_internal.cpp src/output/output_http_internal.cpp
src/io.cpp src/io.cpp
generated/silverlight.js.h generated/player.js.h
generated/embed.js.h
generated/html5.js.h generated/html5.js.h
generated/flash_strobe.js.h generated/flash_strobe.js.h
generated/theoplayer.js.h
generated/jwplayer.js.h
generated/polytrope.js.h
generated/dashjs.js.h generated/dashjs.js.h
generated/videojs.js.h generated/videojs.js.h
generated/img.js.h generated/webrtc.js.h
generated/playerdash.js.h generated/player_dash.js.h
generated/playerdashlic.js.h generated/player_dash_lic.js.h
generated/playervideo.js.h generated/player_video.js.h
generated/playerhlsvideo.js.h generated/player_webrtc.js.h
generated/core.js.h generated/skin_default.css.h
generated/mist.css.h generated/skin_dev.css.h
generated/skin_videojs.css.h
) )
set_target_properties(MistOutHTTP set_target_properties(MistOutHTTP
PROPERTIES COMPILE_DEFINITIONS "OUTPUTTYPE=\"output_http_internal.h\"" PROPERTIES COMPILE_DEFINITIONS "OUTPUTTYPE=\"output_http_internal.h\""
@ -544,69 +541,61 @@ endif()
######################################## ########################################
# Embed Code # # Embed Code #
######################################## ########################################
add_custom_command(OUTPUT generated/silverlight.js.h # main
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/silverlight.js silverlight_js generated/silverlight.js.h add_custom_command(OUTPUT generated/player.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/silverlight.js COMMAND ./sourcery ${SOURCE_DIR}/embed/min/player.js player_js generated/player.js.h
) DEPENDS sourcery ${SOURCE_DIR}/embed/min/player.js
add_custom_command(OUTPUT generated/embed.js.h
COMMAND ./sourcery ${SOURCE_DIR}/src/embed.js embed_js generated/embed.js.h
DEPENDS sourcery ${SOURCE_DIR}/src/embed.js
) )
# wrappers
add_custom_command(OUTPUT generated/html5.js.h add_custom_command(OUTPUT generated/html5.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/html5.js html5_js generated/html5.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/html5.js html5_js generated/html5.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/html5.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/html5.js
) )
add_custom_command(OUTPUT generated/flash_strobe.js.h add_custom_command(OUTPUT generated/flash_strobe.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/flash_strobe.js flash_strobe_js generated/flash_strobe.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/flash_strobe.js flash_strobe_js generated/flash_strobe.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/flash_strobe.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/flash_strobe.js
)
add_custom_command(OUTPUT generated/theoplayer.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/theoplayer.js theoplayer_js generated/theoplayer.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/theoplayer.js
)
add_custom_command(OUTPUT generated/jwplayer.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/jwplayer.js jwplayer_js generated/jwplayer.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/jwplayer.js
)
add_custom_command(OUTPUT generated/polytrope.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/polytrope.js polytrope_js generated/polytrope.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/polytrope.js
) )
add_custom_command(OUTPUT generated/dashjs.js.h add_custom_command(OUTPUT generated/dashjs.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/dashjs.js dash_js generated/dashjs.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/dashjs.js dash_js generated/dashjs.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/dashjs.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/dashjs.js
) )
add_custom_command(OUTPUT generated/videojs.js.h add_custom_command(OUTPUT generated/videojs.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/videojs.js video_js generated/videojs.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/videojs.js video_js generated/videojs.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/videojs.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/videojs.js
) )
add_custom_command(OUTPUT generated/img.js.h add_custom_command(OUTPUT generated/webrtc.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/img.js img_js generated/img.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/webrtc.js webrtc_js generated/webrtc.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/img.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/webrtc.js
) )
add_custom_command(OUTPUT generated/playerdashlic.js.h # players
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.js.license.js playerdashlic_js generated/playerdashlic.js.h add_custom_command(OUTPUT generated/player_dash_lic.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.js.license.js player_dash_lic_js generated/player_dash_lic.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.js.license.js DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.js.license.js
) )
add_custom_command(OUTPUT generated/playerdash.js.h add_custom_command(OUTPUT generated/player_dash.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.all.min.js playerdash_js generated/playerdash.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.all.min.js player_dash_js generated/player_dash.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.all.min.js DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.all.min.js
) )
add_custom_command(OUTPUT generated/playervideo.js.h add_custom_command(OUTPUT generated/player_video.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/video.min.js playervideo_js generated/playervideo.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/players/video.min.js player_video_js generated/player_video.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/players/video.min.js DEPENDS sourcery ${SOURCE_DIR}/embed/players/video.min.js
) )
add_custom_command(OUTPUT generated/playerhlsvideo.js.h add_custom_command(OUTPUT generated/player_webrtc.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/videojs-contrib-hls.min.js playerhlsvideo_js generated/playerhlsvideo.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/players/webrtc.js player_webrtc_js generated/player_webrtc.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/players/videojs-contrib-hls.min.js DEPENDS sourcery ${SOURCE_DIR}/embed/players/webrtc.js
) )
add_custom_command(OUTPUT generated/core.js.h # css
COMMAND ./sourcery ${SOURCE_DIR}/embed/core.js core_js generated/core.js.h add_custom_command(OUTPUT generated/skin_default.css.h
DEPENDS sourcery ${SOURCE_DIR}/embed/core.js COMMAND ./sourcery ${SOURCE_DIR}/embed/min/skins/default.css skin_default_css generated/skin_default.css.h
DEPENDS sourcery ${SOURCE_DIR}/embed/min/skins/default.css
) )
add_custom_command(OUTPUT generated/mist.css.h add_custom_command(OUTPUT generated/skin_dev.css.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/mist.css mist_css generated/mist.css.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/skins/dev.css skin_dev_css generated/skin_dev.css.h
DEPENDS sourcery ${SOURCE_DIR}/embed/mist.css DEPENDS sourcery ${SOURCE_DIR}/embed/min/skins/dev.css
)
add_custom_command(OUTPUT generated/skin_videojs.css.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/skins/video-js.css skin_videojs_css generated/skin_videojs.css.h
DEPENDS sourcery ${SOURCE_DIR}/embed/skins/video-js.css
) )
######################################## ########################################

122
embed/controls.js vendored Normal file
View file

@ -0,0 +1,122 @@
function MistUI(MistVideo,structure) {
MistVideo.UI = this;
this.elements = [];
this.buildStructure = function(structure){
if (typeof structure == "function") { structure = structure.call(MistVideo); }
if ("if" in structure) {
var result = false;
if (structure.if.call(MistVideo,structure)) {
result = structure.then;
}
else if ("else" in structure) {
result = structure.else;
}
if (!result) { return; }
//append the result with structure options
for (var i in structure) {
if (["if","then","else"].indexOf(i) < 0) {
if (i in result) {
if (!(result[i] instanceof Array)) {
result[i] = [result[i]];
}
result[i] = result[i].concat(structure[i]);
}
else {
result[i] = structure[i];
}
}
}
return this.buildStructure(result);
}
if ("type" in structure) {
if (structure.type in MistVideo.skin.blueprints) {
//create the element; making sure to pass "this" to blueprint function
var container = MistVideo.skin.blueprints[structure.type].call(MistVideo,structure);
if (!container) { return; }
MistUtil.class.add(container,"mistvideo-"+structure.type);
if ("css" in structure) {
var uid = MistUtil.createUnique();
structure.css = [].concat(structure.css); //convert to array; should be in string format with colors already applied
for (var i in structure.css) {
var style = MistUtil.css.createStyle(structure.css[i],uid);
container.appendChild(style);
}
MistUtil.class.add(container,uid);
container.uid = uid;
}
if ("classes" in structure) {
for (var i in structure.classes) {
MistUtil.class.add(container,structure.classes[i]);
}
}
if ("title" in structure) {
container.title = structure.title;
}
if ("style" in structure) {
for (var i in structure.style) {
container.style[i] = structure.style[i];
}
}
if ("children" in structure) {
for (var i in structure.children) {
var child = this.buildStructure(structure.children[i]);
if (child) {
container.appendChild(child);
}
}
}
//save the returned element so they can be killed on unload
MistVideo.UI.elements.push(container);
return container;
}
}
return false;
};
this.build = function(){
return this.buildStructure(structure ? structure : MistVideo.skin.structure.main);
};
var container = this.build();
//apply skin CSS
var uid = MistUtil.createUnique();
var toload = MistVideo.skin.css.length;
if (toload) { container.style.opacity = "0"; }
for (var i in MistVideo.skin.css) {
var style = MistVideo.skin.css[i];
style.callback = function(css) {
this.textContent = MistUtil.css.prependClass(css,uid,true);
toload--;
if (toload <= 0) {
container.style.opacity = "";
}
};
if (style.textContent != "") {
//it has already loaded
style.callback(style.textContent);
}
container.appendChild(style);
}
MistUtil.class.add(container,uid);
//add browser class
var browser = MistUtil.getBrowser();
if (browser) {
MistUtil.class.add(container,"browser-"+browser);
}
return container;
}

File diff suppressed because it is too large Load diff

View file

@ -1 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg3937" height="45" width="45"><defs id="defs3939" /><metadata id="metadata3942"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><g transform="translate(0,-1007.3622)" id="layer1"><g transform="translate(0,-1.109375)" id="g4563"><g id="g4558"><path id="rect3945" transform="translate(0,1007.3622)" d="M 5.15625,10 C 3.6913461,10 2.5,11.191346 2.5,12.65625 l 0,19.6875 C 2.5,33.808654 3.6913461,35 5.15625,35 l 34.6875,0 C 41.308654,35 42.5,33.808654 42.5,32.34375 l 0,-19.6875 C 42.5,11.191346 41.308654,10 39.84375,10 L 5.15625,10 z M 5,12.53125 l 35,0 0,20 -35,0 0,-20 z" style="fill:#fff;fill-opacity:1;stroke:none" /><rect ry="0" y="1019.8622" x="5" height="20" width="35" id="rect3947" style="fill:#fff;fill-opacity:0.39215686;stroke:none" /><path id="path3949" transform="translate(0,1007.3622)" d="m 18.78125,35.40625 c -1.53661,0.379809 -2.971465,0.991557 -4.28125,1.8125 l 15.65625,0 c -1.313005,-0.822961 -2.762825,-1.432953 -4.3125,-1.8125 l -7.0625,0 z" style="fill:#fff;fill-opacity:1;stroke:none" /></g><g id="g4007" transform="matrix(2.0353985,0,0,1.1630828,-99.321734,-141.54581)" style="fill:#000"><path id="rect3958" d="m 65.533646,1001.4758 -2.032932,0 0.662913,0.6629 -2.253903,2.2539 0.707107,0.7071 2.253903,-2.2539 0.662912,0.6629 0,-2.0329 z" style="fill:#fff;fill-opacity:1;stroke:none" /><path id="rect3958-5" d="m 65.533646,1012.84 0,-2.033 -0.662836,0.6629 -2.253901,-2.2539 -0.707104,0.7071 2.253902,2.2539 -0.662906,0.6629 2.032845,1e-4 z" style="fill:#fff;fill-opacity:1;stroke:none" /><path id="rect3958-51" d="m 54.16943,1001.4758 2.032932,0 -0.662913,0.6629 2.253903,2.2539 -0.707107,0.7071 -2.253903,-2.2539 -0.662912,0.6629 0,-2.0329 z" style="fill:#fff;fill-opacity:1;stroke:none" /><path id="rect3958-5-7" d="m 54.16943,1012.84 0,-2.033 0.662836,0.6629 2.253901,-2.2539 0.707104,0.7071 -2.253902,2.2539 0.662906,0.6629 -2.032845,1e-4 z" style="fill:#fff;fill-opacity:1;stroke:none" /></g></g></g></svg> <svg width="45" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g fill="#fff">
<path d="m5.1562 8.8906c-1.4649 0-2.6562 1.1913-2.6562 2.6562v19.688c0 1.4649 1.1913 2.6562 2.6562 2.6562h34.688c1.4649 0 2.6562-1.1913 2.6562-2.6562v-19.688c0-1.4649-1.1913-2.6562-2.6562-2.6562h-34.688m-0.15625 2.5312h35v20h-35v-20"/>
<rect transform="translate(0 -1008.5)" x="5" y="1019.9" width="35" height="20" ry="0" fill-opacity=".39216"/>
<path d="m18.781 34.297c-1.5366 0.37981-2.9715 0.99156-4.2812 1.8125h15.656c-1.313-0.82296-2.7628-1.433-4.3125-1.8125h-7.0625"/>
<g>
<path d="m34.065 14.782h-4.1378l1.3493 0.77101-4.5876 2.6215 1.4392 0.82242 4.5876-2.6215s1.3493 0.77101 1.3493 0.77101v-2.3644"/>
<path d="m34.065 27.999v-2.3645s-1.3491 0.77101-1.3491 0.77101l-4.5876-2.6215-1.4392 0.82242 4.5876 2.6215-1.3493 0.77101 4.1376 1.1631e-4"/>
<path d="m10.935 14.782h4.1378l-1.3493 0.77101 4.5876 2.6215-1.4392 0.82242-4.5876-2.6215s-1.3493 0.77101-1.3493 0.77101v-2.3644"/>
<path d="m10.935 27.999v-2.3645s1.3491 0.77101 1.3491 0.77101l4.5876-2.6215s1.4392 0.82242 1.4392 0.82242l-4.5876 2.6215 1.3493 0.77101-4.1376 1.1631e-4"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,3 @@
<svg width="45" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m2.5 10.928v8.5898l4.9023-2.8008 9.6172 5.7832-9.6172 5.7832-4.9023-2.8008v8.5898h15.031l-4.9004-2.8008 9.8691-5.6387 9.8691 5.6387-4.9004 2.8008h15.031v-8.5898l-4.9023 2.8008-9.6172-5.7832 9.6172-5.7832 4.9023 2.8008v-8.5898h-15.033l4.9023 2.8008-9.8691 5.6387-9.8691-5.6387 4.9023-2.8008z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 402 B

61
embed/imgs/gear.svg Normal file
View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg4659"
height="45"
width="45"
sodipodi:docname="gear.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<sodipodi:namedview
pagecolor="#4cc5a0"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1868"
inkscape:window-height="1049"
id="namedview8"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="6.675088"
inkscape:cx="62.439695"
inkscape:cy="43.399157"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg4659"
inkscape:snap-page="true" />
<defs
id="defs4661" />
<metadata
id="metadata4664">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.50001693;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 24.139007,3.834512 -1.477875,4.3213185 c -1.101822,0.0088 -2.274313,0.1330197 -3.204775,0.3369163 L 17.128868,4.5441522 C 15.631516,4.9971895 14.196746,5.635837 12.858006,6.4452068 l 1.34441,4.3668912 c -0.878085,0.62225 -1.683392,1.341293 -2.400733,2.14357 L 7.6056909,11.132739 c -0.9476032,1.24558 -1.7355288,2.604902 -2.3453935,4.046251 l 3.6523686,2.739277 c -0.3489484,1.021477 -0.5815406,2.079062 -0.6933641,3.15269 L 3.7661466,22.05892 c -0.00716,0.146959 -0.012582,0.293997 -0.016276,0.441084 0.00628,1.417863 0.173364,2.830439 0.4980503,4.21064 l 4.5703436,0.06998 c 0.3217092,1.027112 0.7579402,2.014774 1.3004645,2.944356 l -3.0387575,3.435896 c 0.8950189,1.282782 1.947035,2.448529 3.1315315,3.470075 l 3.735377,-2.628599 c 0.86307,0.64582 1.796153,1.192348 2.781594,1.629243 l -0.436201,4.539419 c 1.475447,0.52082 3.010282,0.854908 4.568716,0.994474 l 1.477875,-4.321319 c 0.05369,0.003 0.107408,0.0057 0.161134,0.0082 1.023951,-0.0061 2.044309,-0.121786 3.043641,-0.345054 l 2.327489,3.948594 c 1.497352,-0.453037 2.932122,-1.091685 4.270862,-1.901055 l -1.34441,-4.366891 c 0.878085,-0.622249 1.683392,-1.341292 2.400733,-2.14357 l 4.195992,1.82293 c 0.947603,-1.245581 1.735529,-2.604903 2.345394,-4.046252 l -3.652368,-2.739276 c 0.348948,-1.021478 0.58154,-2.079063 0.693363,-3.152691 l 4.453156,-0.987963 c 0.0072,-0.146984 0.01258,-0.294022 0.01628,-0.441108 -0.0062,-1.417863 -0.173364,-2.83044 -0.49805,-4.210641 l -4.570344,-0.06998 c -0.321709,-1.027116 -0.75794,-2.014777 -1.300465,-2.94436 l 3.038754,-3.435928 C 37.025006,10.556342 35.97299,9.3905941 34.788493,8.3690481 L 31.053116,10.997647 C 30.190046,10.351826 29.256963,9.8052988 28.271521,9.3684038 L 28.707723,4.8289849 C 27.232276,4.308166 25.697441,3.9740781 24.139007,3.834512 Z"
id="path4541"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccc" />
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,3 @@
<svg width="45" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m24.139 3.834-1.4785 4.3223c-1.1018 0.0088-2.2727 0.13204-3.2031 0.33594l-2.3281-3.9473c-1.4974 0.45304-2.9327 1.091-4.2715 1.9004l1.3457 4.3672c-0.87808 0.62225-1.685 1.3403-2.4023 2.1426l-4.1953-1.8223c-0.9476 1.2456-1.7358 2.6055-2.3457 4.0469l3.6523 2.7383c-0.34895 1.0215-0.58154 2.0787-0.69336 3.1523l-4.4531 0.98828c-0.00716 0.14696-0.011931 0.29432-0.015625 0.44141 0.00628 1.4179 0.17336 2.8307 0.49805 4.2109l4.5703 0.070312c0.32171 1.0271 0.75826 2.0138 1.3008 2.9434l-3.0391 3.4355c0.89502 1.2828 1.9464 2.4492 3.1309 3.4707l3.7363-2.6289c0.86307 0.64582 1.7958 1.192 2.7812 1.6289l-0.43555 4.541c1.4754 0.52082 3.0099 0.85458 4.5684 0.99414l1.4766-4.3223c0.05369 3e-3 0.10838 0.005313 0.16211 0.007812 1.024-0.0061 2.0436-0.12048 3.043-0.34375l2.3281 3.9473c1.4974-0.45304 2.9327-1.091 4.2715-1.9004l-1.3457-4.3672c0.87808-0.62225 1.685-1.3403 2.4023-2.1426l4.1953 1.8223c0.9476-1.2456 1.7358-2.6055 2.3457-4.0469l-3.6523-2.7383c0.34895-1.0215 0.58154-2.0787 0.69336-3.1523l4.4531-0.98828c0.0072-0.14698 0.011925-0.29432 0.015625-0.44141-0.0062-1.4179-0.17336-2.8307-0.49805-4.2109l-4.5703-0.070312c-0.32171-1.0271-0.75826-2.0138-1.3008-2.9434l3.0391-3.4355c-0.89502-1.2828-1.9464-2.4492-3.1309-3.4707l-3.7363 2.6289c-0.86307-0.64582-1.7958-1.192-2.7812-1.6289l0.43555-4.541c-1.4754-0.52082-3.0099-0.85457-4.5684-0.99414zm-1.6387 7.8789a10.786 10.786 0 0 1 10.787 10.787 10.786 10.786 0 0 1-10.787 10.787 10.786 10.786 0 0 1-10.787-10.787 10.786 10.786 0 0 1 10.787-10.787z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

1
embed/imgs/loading.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="100%" width="100%"><path d="m49.998 8.7797e-4c-0.060547 0.0018431-0.12109 0.0037961-0.18164 0.0058593-0.1251 0.0015881-0.25012 0.0061465-0.375 0.013672h-0.001954c-27.388 0.30599-49.432 22.59-49.439 49.98 0.020074 2.6488 0.25061 5.292 0.68945 7.904 3.8792-24.231 24.77-42.065 49.311-42.096v-0.0058582h0.001954c4.3638 3.0803e-4 7.9013-3.5366 7.9021-7.9002 1.474e-4 -2.0958-0.83235-4.106-2.3144-5.5879-1.482-1.482-3.492-2.3145-5.5879-2.3144-6.5007e-4 -7.9369e-8 -0.0013001-7.9369e-8 -0.001954 0" class="semiFill"/></svg>

After

Width:  |  Height:  |  Size: 580 B

View file

@ -11,8 +11,9 @@
id="svg3937" id="svg3937"
height="45" height="45"
width="45" width="45"
inkscape:version="0.91 r13725" inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="loop.svg"> sodipodi:docname="loop.svg"
viewBox="0 0 45 45">
<sodipodi:namedview <sodipodi:namedview
pagecolor="#8bff39" pagecolor="#8bff39"
bordercolor="#666666" bordercolor="#666666"
@ -22,8 +23,8 @@
guidetolerance="10" guidetolerance="10"
inkscape:pageopacity="1" inkscape:pageopacity="1"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1920" inkscape:window-width="1868"
inkscape:window-height="1055" inkscape:window-height="1049"
id="namedview3591" id="namedview3591"
showgrid="false" showgrid="false"
showguides="true" showguides="true"
@ -40,37 +41,17 @@
inkscape:snap-smooth-nodes="true" inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true" inkscape:snap-midpoints="true"
inkscape:zoom="14.833529" inkscape:zoom="14.833529"
inkscape:cx="30.216329" inkscape:cx="21.722684"
inkscape:cy="21.135445" inkscape:cy="25.7616"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="0" inkscape:window-y="0"
inkscape:window-maximized="1" inkscape:window-maximized="1"
inkscape:current-layer="svg3937"> inkscape:current-layer="svg3937"
<sodipodi:guide fit-margin-top="0"
position="0,0" fit-margin-left="0"
orientation="0,45" fit-margin-right="0"
id="guide3615" /> fit-margin-bottom="0"
<sodipodi:guide scale-x="1" />
position="45,0"
orientation="-45,0"
id="guide3617" />
<sodipodi:guide
position="45,45"
orientation="0,-45"
id="guide3619" />
<sodipodi:guide
position="0,45"
orientation="45,0"
id="guide3621" />
<sodipodi:guide
position="0,0"
orientation="-0.70710678,0.70710678"
id="guide3624" />
<sodipodi:guide
position="0,45"
orientation="0.70710678,0.70710678"
id="guide4491" />
</sodipodi:namedview>
<defs <defs
id="defs3939" /> id="defs3939" />
<metadata <metadata
@ -86,11 +67,8 @@
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<path <path
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" style="opacity:1;fill:none;fill-opacity:0.8584475;fill-rule:evenodd;stroke:#ffffff;stroke-width:2.50002718;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 0 0 L 0 45 L 45 45 L 45 0 L 0 0 z M 22.5 11.25 A 11.25 11.25 0 0 1 33.75 22.5 A 11.25 11.25 0 0 1 22.5 33.75 A 11.25 11.25 0 0 1 14.550781 30.449219 L 12.714844 32.285156 L 12.714844 25.785156 L 19.214844 25.785156 L 17.376953 27.623047 A 7.25 7.25 0 0 0 22.5 29.75 A 7.25 7.25 0 0 0 29.75 22.5 A 7.25 7.25 0 0 0 22.5 15.25 A 7.25 7.25 0 0 0 17.376953 17.376953 L 14.550781 14.550781 A 11.25 11.25 0 0 1 22.5 11.25 z " d="M 21.279283,3.749797 A 18.750203,18.750203 0 0 0 8.0304417,9.2511582 L 12.740779,13.961496 A 12.083464,12.083464 0 0 1 21.279283,10.416536 12.083464,12.083464 0 0 1 33.362748,22.5 12.083464,12.083464 0 0 1 21.279283,34.583464 12.083464,12.083464 0 0 1 12.740779,31.038504 l 3.063185,-3.063185 H 4.9705135 V 38.80877 L 8.0304417,35.748842 A 18.750203,18.750203 0 0 0 21.279283,41.250203 18.750203,18.750203 0 0 0 40.029486,22.5 18.750203,18.750203 0 0 0 21.279283,3.749797 Z"
id="rect4511" /> id="path4495"
<path inkscape:connector-curvature="0" />
style="opacity:1;fill:none;fill-opacity:0.8584475;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 22.5 11.25 A 11.25 11.25 0 0 0 14.550781 14.550781 L 17.376953 17.376953 A 7.25 7.25 0 0 1 22.5 15.25 A 7.25 7.25 0 0 1 29.75 22.5 A 7.25 7.25 0 0 1 22.5 29.75 A 7.25 7.25 0 0 1 17.376953 27.623047 L 19.214844 25.785156 L 12.714844 25.785156 L 12.714844 32.285156 L 14.550781 30.449219 A 11.25 11.25 0 0 0 22.5 33.75 A 11.25 11.25 0 0 0 33.75 22.5 A 11.25 11.25 0 0 0 22.5 11.25 z "
id="path4495" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,7 @@
<svg width="45" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path class="stroke semiFill toggle" d="m25.587 5.2036c-1.8324-1.1012-4.2007-0.8622-5.7719 0.77112 0 0-7.7388 8.0444-7.7388 8.0444h-3.418c-1.9537 0-3.547 1.6562-3.547 3.6869v9.5864c0 2.0309 1.5933 3.6871 3.547 3.6871h3.418s7.7388 8.0447 7.7388 8.0447c1.5721 1.634 3.9389 1.8747 5.7719 0.77076v-34.591" fill="none" stroke="#000"/>
<g fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.4142">
<path d="m30.032 27.86 9.8517-9.8517"/>
<path d="m30.032 18.008 9.8517 9.8517"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 590 B

View file

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg2" height="45" width="45"><defs id="defs4" /><metadata id="metadata7"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><g transform="translate(0,-1007.3622)" id="layer1"><g style="fill:#fff" transform="translate(3.0304575,47.729705)" id="g3779"><path id="path3823-7" d="m 4.4695429,998.16377 a 4.0011916,4.0011916 0 0 0 3.749999,3.96873 l 2.2812501,0 a 4.0011916,4.0011916 0 0 0 3.96875,-3.75003 l 0,-32.28123 a 4.0011916,4.0011916 0 0 0 -3.75,-3.96875 l -2.2812501,0 a 4.0011916,4.0011916 0 0 0 -3.968749,3.75 l 0,32.28128 z" style="fill:#fff;fill-opacity:1;stroke:none" /><path id="path3823-7-4" d="m 24.469542,998.1638 a 4.0011916,4.0011916 0 0 0 3.75,3.9687 l 2.28125,0 a 4.0011916,4.0011916 0 0 0 3.96875,-3.75 l 0,-32.28126 a 4.0011916,4.0011916 0 0 0 -3.75,-3.96875 l -2.28125,0 a 4.0011916,4.0011916 0 0 0 -3.96875,3.75 l 0,32.28131 z" style="fill:#fff;fill-opacity:1;stroke:none" /></g></g></svg> <svg width="25" height="25" version="1.1" viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.55556 0 0 .55556 0 -559.65)"><g transform="translate(3.0305 47.73)" fill="#fff"><path d="m4.4695 998.16a4.0012 4.0012 0 0 0 3.75 3.9687h2.2813a4.0012 4.0012 0 0 0 3.9688-3.75v-32.281a4.0012 4.0012 0 0 0-3.75-3.9688h-2.2813a4.0012 4.0012 0 0 0-3.9687 3.75z"/><path d="m24.47 998.16a4.0012 4.0012 0 0 0 3.75 3.9687h2.2812a4.0012 4.0012 0 0 0 3.9688-3.75v-32.281a4.0012 4.0012 0 0 0-3.75-3.9688h-2.2812a4.0012 4.0012 0 0 0-3.9688 3.75z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 569 B

View file

@ -13,10 +13,8 @@
height="45" height="45"
id="svg2" id="svg2"
version="1.1" version="1.1"
inkscape:version="0.48.4 r9939" inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="New document 1"> sodipodi:docname="play.svg">
<defs
id="defs4" />
<sodipodi:namedview <sodipodi:namedview
id="base" id="base"
pagecolor="#000000" pagecolor="#000000"
@ -39,28 +37,34 @@
inkscape:snap-page="true" inkscape:snap-page="true"
inkscape:object-nodes="true" inkscape:object-nodes="true"
inkscape:snap-nodes="false" inkscape:snap-nodes="false"
inkscape:window-width="981" inkscape:window-width="1868"
inkscape:window-height="709" inkscape:window-height="1049"
inkscape:window-x="856" inkscape:window-x="0"
inkscape:window-y="240" inkscape:window-y="0"
inkscape:window-maximized="0"> inkscape:window-maximized="0">
<sodipodi:guide <sodipodi:guide
position="0,0" inkscape:locked="false"
id="guide3785"
orientation="0,45" orientation="0,45"
id="guide3785" /> position="0,0" />
<sodipodi:guide <sodipodi:guide
position="45,0" inkscape:locked="false"
id="guide3787"
orientation="-45,0" orientation="-45,0"
id="guide3787" /> position="45,0" />
<sodipodi:guide <sodipodi:guide
position="45,45" inkscape:locked="false"
id="guide3789"
orientation="0,-45" orientation="0,-45"
id="guide3789" /> position="45,45" />
<sodipodi:guide <sodipodi:guide
position="0,45" inkscape:locked="false"
id="guide3791"
orientation="45,0" orientation="45,0"
id="guide3791" /> position="0,45" />
</sodipodi:namedview> </sodipodi:namedview>
<defs
id="defs4" />
<metadata <metadata
id="metadata7"> id="metadata7">
<rdf:RDF> <rdf:RDF>
@ -69,22 +73,17 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title> <dc:title />
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<g <g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-1007.3622)"> inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path <path
sodipodi:type="inkscape:offset" d="M6.26004984594 3.0550109625C5.27445051914 3.68940862462 4.67905105702 4.78142391497 4.67968264562 5.95354422781C4.67968264562 5.95354422781 4.70004942312 39.0717540916 4.70004942312 39.0717540916C4.70302341604 40.3033886636 5.36331656075 41.439734231 6.43188211452 42.0521884912C7.50044766829 42.6646427515 8.81469531629 42.6600161659 9.87892235656 42.0400537716C9.87892235656 42.0400537716 38.5612768409 25.4802882606 38.5612768409 25.4802882606C39.6181165777 24.8606067582 40.2663250096 23.7262617523 40.2636734301 22.5011460995C40.2610218505 21.2760304467 39.6079092743 20.1445019555 38.5483970356 19.5294009803C38.5483970356 19.5294009803 9.84567577375 2.9709566275 9.84567577375 2.9709566275C8.72898008118 2.32550764609 7.34527425735 2.35794451351 6.26004984594 3.0550109625C6.26004984594 3.0550109625 6.26004984594 3.0550109625 6.26004984594 3.0550109625"
inkscape:radius="0"
inkscape:original="M 10.3125 -6.34375 A 2.9416186 2.9416186 0 0 0 7.90625 -4.875 L -6.21875 19.625 A 2.9416186 2.9416186 0 0 0 -3.65625 24.03125 L 24.625 24.03125 A 2.9416186 2.9416186 0 0 0 27.15625 19.625 L 13 -4.875 A 2.9416186 2.9416186 0 0 0 10.3125 -6.34375 z "
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path3809" id="path3809"
d="M 10.3125,-6.34375 A 2.9416186,2.9416186 0 0 0 7.90625,-4.875 l -14.125,24.5 a 2.9416186,2.9416186 0 0 0 2.5625,4.40625 l 28.28125,0 A 2.9416186,2.9416186 0 0 0 27.15625,19.625 L 13,-4.875 a 2.9416186,2.9416186 0 0 0 -2.6875,-1.46875 z" style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" />
transform="matrix(1.0141827,-0.58553867,0.58553867,1.0141827,-0.48419831,1022.8893)" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

3
embed/imgs/popout.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="45" height="45" version="1.1" viewBox="0 0 45 45" xmlns="http://www.w3.org/2000/svg">
<path d="m24.721 11.075c-12.96 0.049575-32.113 15.432-10.336 28.834-7.6763-7.9825-2.4795-21.824 10.336-22.19v5.5368l15.276-8.862-15.276-8.86v5.5419" fill="none" stroke="#000" stroke-width="1.4142"/>
</svg>

After

Width:  |  Height:  |  Size: 306 B

View file

@ -1,6 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"
@ -9,49 +7,38 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="25"
height="25"
id="svg4659"
version="1.1" version="1.1"
inkscape:version="0.91 r13725" id="svg4659"
sodipodi:docname="speaker.svg"> height="45"
<defs width="45"
id="defs4661" /> sodipodi:docname="speaker.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<sodipodi:namedview <sodipodi:namedview
id="base" pagecolor="#4cc5a0"
pagecolor="#de43da"
bordercolor="#666666" bordercolor="#666666"
borderopacity="1.0" borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="19.289873" inkscape:window-width="1868"
inkscape:cx="-2.0603844" inkscape:window-height="1049"
inkscape:cy="14.079113" id="namedview8"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false" showgrid="false"
inkscape:window-width="1918" fit-margin-top="0"
inkscape:window-height="1040" fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="13.350176"
inkscape:cx="0.054475841"
inkscape:cy="20.133193"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="19" inkscape:window-y="0"
inkscape:window-maximized="0"> inkscape:window-maximized="0"
<sodipodi:guide inkscape:current-layer="svg4659" />
position="0,0" <defs
orientation="0,25" id="defs4661" />
id="guide4151" />
<sodipodi:guide
position="25,0"
orientation="-25,0"
id="guide4153" />
<sodipodi:guide
position="25,25"
orientation="0,-25"
id="guide4155" />
<sodipodi:guide
position="0,25"
orientation="25,0"
id="guide4157" />
</sodipodi:namedview>
<metadata <metadata
id="metadata4664"> id="metadata4664">
<rdf:RDF> <rdf:RDF>
@ -65,19 +52,18 @@
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<g <g
inkscape:label="Layer 1" transform="matrix(1.8,0,0,1.8,0,-1849.252)"
inkscape:groupmode="layer" id="layer1">
id="layer1"
transform="translate(0,-1027.3622)">
<path <path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" id="rect4139"
d="M 0 0 L 0 25 L 25 25 L 25 0 L 0 0 z M 16.955078 2.5175781 C 17.381507 2.5360023 17.80574 2.6612 18.1875 2.890625 L 18.1875 22.109375 C 17.169206 22.722675 15.85386 22.587487 14.980469 21.679688 L 10.681641 17.210938 L 8.7832031 17.210938 C 7.6978331 17.210938 6.8125 16.290409 6.8125 15.162109 L 6.8125 9.8359375 C 6.8125 8.7077375 7.6978331 7.7890625 8.7832031 7.7890625 L 10.681641 7.7890625 L 14.980469 3.3183594 C 15.526019 2.7512344 16.244363 2.4868711 16.955078 2.5175781 z "
transform="translate(0,1027.3622)" transform="translate(0,1027.3622)"
id="rect4139" /> d="M 0,0 V 25 H 25 V 0 Z M 16.955078,2.5175781 C 17.381507,2.5360023 17.80574,2.6612 18.1875,2.890625 v 19.21875 c -1.018294,0.6133 -2.33364,0.478112 -3.207031,-0.429687 l -4.298828,-4.46875 H 8.7832031 c -1.08537,0 -1.9707031,-0.920529 -1.9707031,-2.048829 V 9.8359375 c 0,-1.1282 0.8853331,-2.046875 1.9707031,-2.046875 h 1.8984379 l 4.298828,-4.4707031 c 0.54555,-0.567125 1.263894,-0.8314883 1.974609,-0.8007813 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path <path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 18.187674,1030.2531 c -1.018026,-0.6118 -2.333715,-0.479 -3.206595,0.4284 l -4.299344,4.4691 -1.8988757,0 c -1.0853708,0 -1.9705331,0.9201 -1.9705331,2.0483 l 0,5.3258 c 0,1.1283 0.8851623,2.0484 1.9705331,2.0484 l 1.8988757,0 4.299344,4.4693 c 0.873391,0.9078 2.188301,1.0415 3.206595,0.4282 l 0,-19.2175 z"
id="rect4574" id="rect4574"
d="m 18.187674,1030.2531 c -1.018026,-0.6118 -2.333715,-0.479 -3.206595,0.4284 l -4.299344,4.4691 H 8.7828593 c -1.0853708,0 -1.9705331,0.9201 -1.9705331,2.0483 v 5.3258 c 0,1.1283 0.8851623,2.0484 1.9705331,2.0484 h 1.8988757 l 4.299344,4.4693 c 0.873391,0.9078 2.188301,1.0415 3.206595,0.4282 z"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

59
embed/imgs/speaker_.svg Normal file
View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg4659"
height="45"
width="45"
sodipodi:docname="speaker_.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<sodipodi:namedview
pagecolor="#4cc5a0"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1868"
inkscape:window-height="1049"
id="namedview8"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="6.675088"
inkscape:cx="-34.245466"
inkscape:cy="20.740429"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg4659" />
<defs
id="defs4661" />
<metadata
id="metadata4664">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:3.81837654;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 32.737813,5.2037363 c -1.832447,-1.10124 -4.200687,-0.8622 -5.771871,0.77112 0,0 -7.738819,8.0443797 -7.738819,8.0443797 0,0 -3.417976,0 -3.417976,0 -1.953668,0 -3.54696,1.65618 -3.54696,3.68694 0,0 0,9.58644 0,9.58644 0,2.03094 1.593292,3.68712 3.54696,3.68712 0,0 3.417976,0 3.417976,0 0,0 7.738819,8.04474 7.738819,8.04474 1.572104,1.63404 3.938942,1.8747 5.771871,0.77076 0,0 0,-34.5914997 0,-34.5914997 z"
id="rect4574-3" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg4659" height="25" width="25"> <defs id="defs4661" /> <metadata id="metadata4664"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title></dc:title> </cc:Work> </rdf:RDF> </metadata> <g transform="translate(0,-1027.3622)" id="layer1"> <path id="rect4139" transform="translate(0,1027.3622)" d="M 0 0 L 0 25 L 25 25 L 25 0 L 0 0 z M 16.955078 2.5175781 C 17.381507 2.5360023 17.80574 2.6612 18.1875 2.890625 L 18.1875 22.109375 C 17.169206 22.722675 15.85386 22.587487 14.980469 21.679688 L 10.681641 17.210938 L 8.7832031 17.210938 C 7.6978331 17.210938 6.8125 16.290409 6.8125 15.162109 L 6.8125 9.8359375 C 6.8125 8.7077375 7.6978331 7.7890625 8.7832031 7.7890625 L 10.681641 7.7890625 L 14.980469 3.3183594 C 15.526019 2.7512344 16.244363 2.4868711 16.955078 2.5175781 z " style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> <path id="rect4574" d="m 18.187674,1030.2531 c -1.018026,-0.6118 -2.333715,-0.479 -3.206595,0.4284 l -4.299344,4.4691 -1.8988757,0 c -1.0853708,0 -1.9705331,0.9201 -1.9705331,2.0483 l 0,5.3258 c 0,1.1283 0.8851623,2.0484 1.9705331,2.0484 l 1.8988757,0 4.299344,4.4693 c 0.873391,0.9078 2.188301,1.0415 3.206595,0.4282 l 0,-19.2175 z" style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> </g></svg> <?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg4659" height="25" width="25"> <defs id="defs4661" /> <metadata id="metadata4664"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title></dc:title> </cc:Work> </rdf:RDF> </metadata> <path d="m 32.737813,5.2037363 c -1.832447,-1.10124 -4.200687,-0.8622 -5.771871,0.77112 0,0 -7.738819,8.0443797 -7.738819,8.0443797 0,0 -3.417976,0 -3.417976,0 -1.953668,0 -3.54696,1.65618 -3.54696,3.68694 0,0 0,9.58644 0,9.58644 0,2.03094 1.593292,3.68712 3.54696,3.68712 0,0 3.417976,0 3.417976,0 0,0 7.738819,8.04474 7.738819,8.04474 1.572104,1.63404 3.938942,1.8747 5.771871,0.77076 0,0 0,-34.5914997 0,-34.5914997 z" class="stroke semiFill toggle"></path></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,4 @@
<svg width="45" height="45" version="1.1" viewBox="0 0 45 45" xmlns="http://www.w3.org/2000/svg">
<path d="m8.4925 18.786c-3.9578 1.504-6.4432 3.632-6.4434 5.9982 2.183e-4 4.1354 7.5562 7.5509 17.399 8.1467v4.7777l10.718-6.2573-10.718-6.2529v4.5717c-6.9764-0.4712-12.229-2.5226-12.227-4.9859 6.693e-4 -0.72127 0.45868-1.4051 1.2714-2.0267zm28.015 0v3.9715c0.81164 0.62126 1.2685 1.3059 1.2692 2.0267-0.0014 1.4217-1.791 2.75-4.8021 3.6968-2.0515 0.82484-0.93693 3.7696 1.2249 2.9659 5.3088-1.8593 8.7426-3.8616 8.7514-6.6627-1.26e-4 -2.3662-2.4856-4.4942-6.4434-5.9982z"/>
<rect x="10.166" y="7.7911" width="24.668" height="15.432" fill="none" stroke="#000" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 694 B

19
embed/imgs/timeout.svg Normal file
View file

@ -0,0 +1,19 @@
<svg width="25" height="25" version="1.1" viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg">
<defs>
<mask id="a">
<rect x="0" y="0" width="25" height="25" fill="#fff"/>
<rect x="-5" y="-5" width="17.5" height="35" fill="#000" transform="rotate(180,12.5,12.5)">
<animateTransform attributeName="transform" type="rotate" from="0,12.5,12.5" to="180,12.5,12.5" dur="5" repeatCount="1"/>
</rect>
<rect x="0" y="0" width="12.5" height="25" fill="#fff"/>
<rect x="-5" y="-5" width="17.5" height="35" fill="#000" transform="rotate(360,12.5,12.5)">
<animate attributeType="CSS" attributeName="opacity" from="0" to="1" dur="10" calcMode="discrete" repeatCount="1" />
<animateTransform attributeName="transform" type="rotate" from="180,12.5,12.5" to="360,12.5,12.5" begin="5" dur="5" repeatCount="1"/>
</rect>
<circle cx="12.5" cy="12.5" r="8" fill="#000"/>
</mask>
</defs>
<g mask="url(#a)">
<circle cx="12.5" cy="12.5" r="12.5" class="fill"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -9,14 +9,25 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30" width="65.000015"
height="65" height="30"
id="svg3937" id="svg3937"
version="1.1" version="1.1"
inkscape:version="0.91 r13725" inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="volume.svg"> sodipodi:docname="volume.svg">
<defs <defs
id="defs3939" /> id="defs3939">
<mask
maskUnits="userSpaceOnUse"
id="mask4516">
<path
inkscape:connector-curvature="0"
id="path4518"
d="m -14.830002,1027.1553 58.614316,-18.8626 c 1.795162,-0.5777 3.231854,0.5139 3.231854,2.1982 v 19.0111 c 0,1.3165 -1.098401,2.3764 -2.462782,2.3764 h -59.106775 c -2.946621,0 -3.449835,-3.6727 -0.276613,-4.7231 z"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.96767992;stroke-opacity:1"
sodipodi:nodetypes="cccsscc" />
</mask>
</defs>
<sodipodi:namedview <sodipodi:namedview
id="base" id="base"
pagecolor="#dc39f7" pagecolor="#dc39f7"
@ -24,16 +35,16 @@
borderopacity="1.0" borderopacity="1.0"
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="4" inkscape:zoom="14.580651"
inkscape:cx="-36.653402" inkscape:cx="30.208524"
inkscape:cy="38.029112" inkscape:cy="17.533448"
inkscape:document-units="px" inkscape:document-units="px"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="false" showgrid="false"
inkscape:snap-bbox="true" inkscape:snap-bbox="true"
inkscape:bbox-paths="false" inkscape:bbox-paths="false"
inkscape:window-width="1920" inkscape:window-width="1868"
inkscape:window-height="1055" inkscape:window-height="1049"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="0" inkscape:window-y="0"
inkscape:window-maximized="1" inkscape:window-maximized="1"
@ -41,7 +52,13 @@
inkscape:object-paths="true" inkscape:object-paths="true"
inkscape:snap-page="true" inkscape:snap-page="true"
inkscape:snap-global="true" inkscape:snap-global="true"
borderlayer="true" /> borderlayer="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
showguides="true"
inkscape:guide-bbox="true" />
<metadata <metadata
id="metadata3942"> id="metadata3942">
<rdf:RDF> <rdf:RDF>
@ -50,7 +67,7 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title /> <dc:title></dc:title>
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
@ -58,16 +75,20 @@
inkscape:label="Layer 1" inkscape:label="Layer 1"
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-987.3622)"> transform="translate(17.50001,-1004.8622)">
<path <path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;enable-background:accumulate" inkscape:connector-curvature="0"
d="m 0,1052.3622 0,-65 5,0 c -0.172588,0 -0.337256,0.0295 -0.5,0.0625 -0.489111,0.1 -0.942345,0.31725 -1.28125,0.65625 -0.226206,0.2262 -0.404743,0.5135 -0.53125,0.8125 -0.126507,0.2991 -0.1875,0.62365 -0.1875,0.96875 0,0 0.075978,0.44705 0.1875,0.78125 l 19.84375,59.49995 c 0.142777,1.2451 1.185414,2.2188 2.46875,2.2188 l -25,0 z m 25,0 c 0.877549,0 1.647663,-0.441 2.09375,-1.125 0.06381,-0.098 0.1062,-0.2046 0.15625,-0.3125 0.02962,-0.062 0.06926,-0.1225 0.09375,-0.1875 0.04501,-0.1212 0.06741,-0.2459 0.09375,-0.375 0.009,-0.044 0.02457,-0.08 0.03125,-0.125 0.01878,-0.1235 0.03125,-0.2462 0.03125,-0.375 l 0,-60 c 0,-1.385 -1.114999,-2.5 -2.5,-2.5 l 5,0 0,65 -5,0 z" id="path4505"
id="rect4674" d="m -14.830002,1027.1553 58.614316,-18.8626 c 1.795162,-0.5777 3.231854,0.5139 3.231854,2.1982 v 19.0111 c 0,1.3165 -1.098401,2.3764 -2.462782,2.3764 h -59.106775 c -2.946621,0 -3.449835,-3.6727 -0.276613,-4.7231 z"
inkscape:connector-curvature="0" /> style="fill:none;fill-opacity:1;stroke:#fdfdfd;stroke-width:0.96767992;stroke-opacity:1"
<path sodipodi:nodetypes="cccsscc" />
style="fill:#ffffff;fill-opacity:1;stroke:none" <rect
d="m 25,1052.3617 c -1.283336,0 -2.325973,-0.9737 -2.46875,-2.2187 L 2.6875,990.6429 C 2.575978,990.3087 2.5,989.8617 2.5,989.8617 c 0,-0.3451 0.060993,-0.6697 0.1875,-0.9688 0.126507,-0.299 0.305044,-0.5863 0.53125,-0.8125 0.338905,-0.339 0.792139,-0.5562 1.28125,-0.6562 0.162744,-0.033 0.327412,-0.062 0.5,-0.062 l 20,0 c 1.385001,0 2.5,1.115 2.5,2.5 l 0,60 c 0,0.1288 -0.01247,0.2515 -0.03125,0.375 -0.0067,0.045 -0.02225,0.081 -0.03125,0.125 -0.02634,0.1292 -0.04874,0.2538 -0.09375,0.375 -0.02449,0.065 -0.06413,0.1252 -0.09375,0.1875 -0.05005,0.1079 -0.09244,0.2145 -0.15625,0.3125 -0.446087,0.684 -1.216201,1.125 -2.09375,1.125 z m 0,-1.2187 c 0.474106,0 0.864734,-0.2114 1.09375,-0.5625 -0.02112,0.032 -0.0059,-0.01 0.0625,-0.1563 a 1.204452,1.204452 0 0 1 0,-0.031 c 0.0235,-0.049 0.05198,-0.052 0.0625,-0.062 0.0055,-0.016 0.0094,-0.035 0,-0.031 0.0017,-0.01 0.0055,-0.061 0.03125,-0.1875 0.008,-0.039 0.02555,-0.039 0.03125,-0.062 0.0098,-0.066 0.0055,-0.1027 0,-0.094 -0.0016,-0.03 0,-0.068 0,-0.094 l 0,-60 c 0,-0.7386 -0.542617,-1.2813 -1.28125,-1.2813 l -20,0 c -0.035353,0 -0.105322,0 -0.25,0.031 -0.296863,0.061 -0.546343,0.1713 -0.6875,0.3125 -0.089394,0.089 -0.205263,0.258 -0.28125,0.4375 -0.055315,0.1308 -0.058661,0.2832 -0.0625,0.4687 -2.52e-4,0.012 0,0.019 0,0.031 0.027982,0.1353 0.082499,0.2789 0.125,0.4062 l 19.84375,59.5005 a 1.204452,1.204452 0 0 1 0.03125,0.25 c 0.07527,0.6564 0.607054,1.125 1.28125,1.125 z" style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.60986549;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4697-6" id="rect4507"
inkscape:connector-curvature="0" /> width="33.400429"
height="30"
x="-17.50001"
y="1004.8622"
mask="url(#mask4516)" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -1,38 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="100" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<svg <defs>
xmlns:dc="http://purl.org/dc/elements/1.1/" <mask id="a" maskUnits="userSpaceOnUse">
xmlns:cc="http://creativecommons.org/ns#" <rect width="57.288" height="30" fill="#fff" fill-opacity=".74439"/>
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" </mask>
xmlns:svg="http://www.w3.org/2000/svg" </defs>
xmlns="http://www.w3.org/2000/svg" <path d="m6.202 33.254 86.029-28.394c2.6348-0.86966 4.7433 0.77359 4.7433 3.3092v28.617c0 1.9819-1.6122 3.5773-3.6147 3.5773h-86.75c-4.3249 0-5.0634-5.5287-0.40598-7.1098" fill="none" stroke="#fdfdfd" stroke-width="6.0417"/>
version="1.1" <g transform="translate(16.609 9.9991)" fill="#fff" mask="url(#a)">
id="svg3937" <path d="m4.2605 21.935 55.47-18.308c1.6989-0.56074 3.0584 0.4988 3.0584 2.1337v18.452c0 1.2779-1.0395 2.3066-2.3307 2.3066h-55.935c-2.7886 0-3.2648-3.5648-0.26177-4.5843" fill="#fff"/>
height="65"
width="30">
<defs
id="defs3939" />
<metadata
id="metadata3942">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-987.3622)"
id="layer1">
<path
id="rect4674"
d="m 0,1052.3622 0,-65 5,0 c -0.172588,0 -0.337256,0.0295 -0.5,0.0625 -0.489111,0.1 -0.942345,0.31725 -1.28125,0.65625 -0.226206,0.2262 -0.404743,0.5135 -0.53125,0.8125 -0.126507,0.2991 -0.1875,0.62365 -0.1875,0.96875 0,0 0.075978,0.44705 0.1875,0.78125 l 19.84375,59.49995 c 0.142777,1.2451 1.185414,2.2188 2.46875,2.2188 l -25,0 z m 25,0 c 0.877549,0 1.647663,-0.441 2.09375,-1.125 0.06381,-0.098 0.1062,-0.2046 0.15625,-0.3125 0.02962,-0.062 0.06926,-0.1225 0.09375,-0.1875 0.04501,-0.1212 0.06741,-0.2459 0.09375,-0.375 0.009,-0.044 0.02457,-0.08 0.03125,-0.125 0.01878,-0.1235 0.03125,-0.2462 0.03125,-0.375 l 0,-60 c 0,-1.385 -1.114999,-2.5 -2.5,-2.5 l 5,0 0,65 -5,0 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;enable-background:accumulate" />
<path
id="path4697-6"
d="m 25,1052.3617 c -1.283336,0 -2.325973,-0.9737 -2.46875,-2.2187 L 2.6875,990.6429 C 2.575978,990.3087 2.5,989.8617 2.5,989.8617 c 0,-0.3451 0.060993,-0.6697 0.1875,-0.9688 0.126507,-0.299 0.305044,-0.5863 0.53125,-0.8125 0.338905,-0.339 0.792139,-0.5562 1.28125,-0.6562 0.162744,-0.033 0.327412,-0.062 0.5,-0.062 l 20,0 c 1.385001,0 2.5,1.115 2.5,2.5 l 0,60 c 0,0.1288 -0.01247,0.2515 -0.03125,0.375 -0.0067,0.045 -0.02225,0.081 -0.03125,0.125 -0.02634,0.1292 -0.04874,0.2538 -0.09375,0.375 -0.02449,0.065 -0.06413,0.1252 -0.09375,0.1875 -0.05005,0.1079 -0.09244,0.2145 -0.15625,0.3125 -0.446087,0.684 -1.216201,1.125 -2.09375,1.125 z m 0,-1.2187 c 0.474106,0 0.864734,-0.2114 1.09375,-0.5625 -0.02112,0.032 -0.0059,-0.01 0.0625,-0.1563 a 1.204452,1.204452 0 0 1 0,-0.031 c 0.0235,-0.049 0.05198,-0.052 0.0625,-0.062 0.0055,-0.016 0.0094,-0.035 0,-0.031 0.0017,-0.01 0.0055,-0.061 0.03125,-0.1875 0.008,-0.039 0.02555,-0.039 0.03125,-0.062 0.0098,-0.066 0.0055,-0.1027 0,-0.094 -0.0016,-0.03 0,-0.068 0,-0.094 l 0,-60 c 0,-0.7386 -0.542617,-1.2813 -1.28125,-1.2813 l -20,0 c -0.035353,0 -0.105322,0 -0.25,0.031 -0.296863,0.061 -0.546343,0.1713 -0.6875,0.3125 -0.089394,0.089 -0.205263,0.258 -0.28125,0.4375 -0.055315,0.1308 -0.058661,0.2832 -0.0625,0.4687 -2.52e-4,0.012 0,0.019 0,0.031 0.027982,0.1353 0.082499,0.2789 0.125,0.4062 l 19.84375,59.5005 a 1.204452,1.204452 0 0 1 0.03125,0.25 c 0.07527,0.6564 0.607054,1.125 1.28125,1.125 z"
style="fill:#ffffff;fill-opacity:1;stroke:none" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 731 B

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100" height="50" version="1.1" viewBox="0 0 100 50" xmlns="http://www.w3.org/2000/svg">
<rect y="21" width="100" height="8" fill="#fff"/>
<ellipse cx="50" cy="25" rx="10" ry="10" fill="#008000"/>
<rect y="21" width="50" height="8" fill="#008000"/>
</svg>

After

Width:  |  Height:  |  Size: 312 B

1
embed/min/player.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,92 @@
.mistvideo{display:inline-block;position:relative;color:$stroke;font-family:sans-serif;background-color:#000;justify-content:center;align-items:center}
.mistvideo-controls{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.mistvideo.novideo{overflow:visible}
.mistvideo-video{overflow:hidden}
svg.icon.loading{z-index:-1;position:absolute;top:0;left:0;right:0;bottom:0;margin:auto;opacity:0}
[data-loading]{position:relative}
[data-loading=stalled] svg.icon.loading{transition:opacity 0s 3s}
[data-loading] svg.icon.loading{z-index:2;opacity:1}
[data-loading-css] .mistvideo-controls{display:none}
[data-hidecursor],[data-hidecursor] *,[data-hidecursor] .mistvideo-pointer{cursor:none}
.mistvideo-error{display:none;position:absolute;top:0;left:0;right:0;bottom:0;background-color:$background;align-items:center;justify-content:center;text-align:center;z-index:2;cursor:default;min-height:fit-content;min-width:fit-content}
.mistvideo-error.show{display:flex}
.mistvideo-error .message{max-width:80%}
.mistvideo-error .message .details table{text-align:left}
.mistvideo-controls button,.mistvideo-error button,.mistvideo-video:not(.video-js) button{color:$stroke;border:1px solid $semiFill;background-color:$background;margin:.25em;padding:.5em 1em;opacity:.5;cursor:pointer}
button:hover{opacity:1}
select{background-color:transparent;color:$stroke;border:none;margin:0 .5em;font-size:inherit;cursor:pointer;-ms-background-color:red}
select>option{background-color:$background}
.browser-edge select,.browser-safari select{border:1px solid $semiFill;border-top:none;border-left:none;margin-top:2px}
@keyframes spin{
0%{transform:rotate(0)}
100%{transform:rotate(360deg)}
}
[data-fullscreen]{position:fixed;top:0;left:0;right:0;bottom:0;width:100%!important;height:100%!important}
video{display:block;flex-shrink:0}
table{color:inherit;font-size:inherit;font-style:inherit}
audio:not([controls]){display:block!important}
.mistvideo-padding{padding:5px 10px}
.mistvideo-pointer{cursor:pointer}
.description{color:$semiFill;font-size:.9em}
.mistvideo-container{display:flex;flex-wrap:nowrap}
.mistvideo-container.mistvideo{display:inline-flex;max-width:100%}
.mistvideo-container.mistvideo-row{flex-direction:row}
.mistvideo-container.mistvideo-column{flex-direction:column}
.mistvideo-container.mistvideo-center{justify-content:center}
.mistvideo-align-right{margin-left:auto;margin-right:0}
.hover_window_container.pos>.outer_window{position:absolute;pointer-events:none;overflow:hidden}
.hover_window_container.pos>.outer_window>.inner_window{position:absolute;pointer-events:all}
.hover_window_container:not(:hover)>.outer_window>.inner_window,.hover_window_container>.outer_window[data-hidecursor]>.inner_window{transition:all .5s ease-in .5s}
.outer_window>.inner_window{flex-grow:1}
.hover_window_container>.outer_window>.inner_window>*{flex-shrink:0}
.mistvideo-draggable{overflow:hidden}
.mistvideo-tooltip{position:absolute;background-color:$background;padding:.5em 1em;z-index:2}
:not(:hover)>.mistvideo-tooltip{transition:opacity .25s ease-in}
.mistvideo-tooltip .triangle{border:10px solid $background;position:absolute}
.mistvideo-tracks label{display:block}
.mistvideo-tracks label>span{margin-right:1em}
a{color:$accent}
.mistvideo-log .logs{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}
.mistvideo-placeholder{max-width:100%;max-height:100%}
.mistvideo-topright{position:absolute;top:0;right:0}
.mistvideo-topleft{position:absolute;top:0;left:0}
.mistvideo-delay-display{animation:appear 1s;animation-iteration-count:1;animation-timing-function:steps(1,end)}
@keyframes appear{
from{opacity:0}
to{opacity:1}
}
svg.icon{display:block;stroke-width:$strokeWidth;fill:none;stroke:none}
svg.icon .fill,svg.icon.fill{fill:$fill}
svg.icon .semiFill,svg.icon.semiFill{fill:$semiFill}
svg.icon .stroke,svg.icon.stroke{stroke:$stroke;vector-effect:non-scaling-stroke}
svg.icon.off .toggle .fill,svg.icon.off .toggle .semiFill,svg.icon.off .toggle.fill,svg.icon.off .toggle.semiFill{fill:none}
svg.icon .spin,svg.icon.spin{animation:spin 1.5s infinite linear;transform-origin:50% 50%}
.mistvideo{line-height:1.2;font-size:14.5px}
.mistvideo svg{margin:2.5px}
.mistvideo-background{background-color:$background}
.mistvideo-totalTime:before{content:'/';margin:.2em}
.mistvideo-progress{padding:10px 0;margin:-10px 0;z-index:2}
.mistvideo-progress>*{height:2px;background-color:$progressBackground;opacity:.95;position:relative}
.mistvideo-novideo .mistvideo-progress>*,.mistvideo-progress:hover>*{height:10px}
.mistvideo-progress:not(:hover)>*{transition:height .25s ease-in .5s}
.mistvideo-progress .bar{height:inherit;width:0;position:absolute;border-right:inherit;background-color:$accent;z-index:2}
.mistvideo-progress .buffer{height:inherit;width:0;position:absolute;background-color:$semiFill}
.mistvideo-progress .bar:after{content:'';border:5px solid $accent;border-radius:5px;position:absolute;right:-5px;top:50%;transform:translateY(-50%)}
.mistvideo-play[data-state=playing] svg.play{display:none}
.mistvideo-play[data-state=paused] svg.pause{display:none}
.mistvideo-main{align-items:center}
svg.icon.timeout{display:inline-block;height:1em;width:1em;margin:0;margin-right:.25em;vertical-align:top}
.mist.largeplay,.mist.muted{position:absolute;opacity:.5}
.mist.largeplay{top:50%;left:0;right:0;margin:auto;transform:translateY(-50%)}
.mist.muted{top:0;right:0;margin:1em}
.mistvideo-secondaryVideo{z-index:1;width:50%;height:50%}
.mistvideo-polling{display:inline-block;position:relative;width:25px;height:25px}
.mistvideo-polling svg.icon.loading{z-index:0;opacity:1}
.mistvideo[data-show-submenu] .mistvideo-submenu{right:5px}
.mistvideo[data-show-submenu] .mistvideo-controls{bottom:0}
.mistvideo-error[data-passive]{bottom:auto;left:auto;margin:.5em;padding:.5em}
.mistvideo-error[data-passive] .message{max-width:none}
.mistvideo-error .mistvideo-buttoncontainer{display:flex;flex-flow:row nowrap;justify-content:center}
.mistvideo-error .mistvideo-buttoncontainer .mistvideo-button{white-space:nowrap}
.browser-ie .mist.icon.loading{animation:spin 1.5s infinite linear;transform-origin:50% 50%}
.browser-ie .mist.icon.loading .spin{animation:none}

116
embed/min/skins/dev.css Normal file
View file

@ -0,0 +1,116 @@
.mistvideo{display:inline-block;position:relative;color:$stroke;font-family:sans-serif;background-color:#000;justify-content:center;align-items:center}
.mistvideo-controls{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.mistvideo.novideo{overflow:visible}
.mistvideo-video{overflow:hidden}
svg.icon.loading{z-index:-1;position:absolute;top:0;left:0;right:0;bottom:0;margin:auto;opacity:0}
[data-loading]{position:relative}
[data-loading=stalled] svg.icon.loading{transition:opacity 0s 3s}
[data-loading] svg.icon.loading{z-index:2;opacity:1}
[data-loading-css] .mistvideo-controls{display:none}
[data-hidecursor],[data-hidecursor] *,[data-hidecursor] .mistvideo-pointer{cursor:none}
.mistvideo-error{display:none;position:absolute;top:0;left:0;right:0;bottom:0;background-color:$background;align-items:center;justify-content:center;text-align:center;z-index:2;cursor:default;min-height:fit-content;min-width:fit-content}
.mistvideo-error.show{display:flex}
.mistvideo-error .message{max-width:80%}
.mistvideo-error .message .details table{text-align:left}
.mistvideo-controls button,.mistvideo-error button,.mistvideo-video:not(.video-js) button{color:$stroke;border:1px solid $semiFill;background-color:$background;margin:.25em;padding:.5em 1em;opacity:.5;cursor:pointer}
button:hover{opacity:1}
select{background-color:transparent;color:$stroke;border:none;margin:0 .5em;font-size:inherit;cursor:pointer;-ms-background-color:red}
select>option{background-color:$background}
.browser-edge select,.browser-safari select{border:1px solid $semiFill;border-top:none;border-left:none;margin-top:2px}
@keyframes spin{
0%{transform:rotate(0)}
100%{transform:rotate(360deg)}
}
[data-fullscreen]{position:fixed;top:0;left:0;right:0;bottom:0;width:100%!important;height:100%!important}
video{display:block;flex-shrink:0}
table{color:inherit;font-size:inherit;font-style:inherit}
audio:not([controls]){display:block!important}
.mistvideo-padding{padding:5px 10px}
.mistvideo-pointer{cursor:pointer}
.description{color:$semiFill;font-size:.9em}
.mistvideo-container{display:flex;flex-wrap:nowrap}
.mistvideo-container.mistvideo{display:inline-flex;max-width:100%}
.mistvideo-container.mistvideo-row{flex-direction:row}
.mistvideo-container.mistvideo-column{flex-direction:column}
.mistvideo-container.mistvideo-center{justify-content:center}
.mistvideo-align-right{margin-left:auto;margin-right:0}
.hover_window_container.pos>.outer_window{position:absolute;pointer-events:none;overflow:hidden}
.hover_window_container.pos>.outer_window>.inner_window{position:absolute;pointer-events:all}
.hover_window_container:not(:hover)>.outer_window>.inner_window,.hover_window_container>.outer_window[data-hidecursor]>.inner_window{transition:all .5s ease-in .5s}
.outer_window>.inner_window{flex-grow:1}
.hover_window_container>.outer_window>.inner_window>*{flex-shrink:0}
.mistvideo-draggable{overflow:hidden}
.mistvideo-tooltip{position:absolute;background-color:$background;padding:.5em 1em;z-index:2}
:not(:hover)>.mistvideo-tooltip{transition:opacity .25s ease-in}
.mistvideo-tooltip .triangle{border:10px solid $background;position:absolute}
.mistvideo-tracks label{display:block}
.mistvideo-tracks label>span{margin-right:1em}
a{color:$accent}
.mistvideo-log .logs{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}
.mistvideo-placeholder{max-width:100%;max-height:100%}
.mistvideo-topright{position:absolute;top:0;right:0}
.mistvideo-topleft{position:absolute;top:0;left:0}
.mistvideo-delay-display{animation:appear 1s;animation-iteration-count:1;animation-timing-function:steps(1,end)}
@keyframes appear{
from{opacity:0}
to{opacity:1}
}
svg.icon{display:block;stroke-width:$strokeWidth;fill:none;stroke:none}
svg.icon .fill,svg.icon.fill{fill:$fill}
svg.icon .semiFill,svg.icon.semiFill{fill:$semiFill}
svg.icon .stroke,svg.icon.stroke{stroke:$stroke;vector-effect:non-scaling-stroke}
svg.icon.off .toggle .fill,svg.icon.off .toggle .semiFill,svg.icon.off .toggle.fill,svg.icon.off .toggle.semiFill{fill:none}
svg.icon .spin,svg.icon.spin{animation:spin 1.5s infinite linear;transform-origin:50% 50%}
.mistvideo{line-height:1.2;font-size:14.5px}
.mistvideo svg{margin:2.5px}
.mistvideo-background{background-color:$background}
.mistvideo-totalTime:before{content:'/';margin:.2em}
.mistvideo-progress{padding:10px 0;margin:-10px 0;z-index:2}
.mistvideo-progress>*{height:2px;background-color:$progressBackground;opacity:.95;position:relative}
.mistvideo-novideo .mistvideo-progress>*,.mistvideo-progress:hover>*{height:10px}
.mistvideo-progress:not(:hover)>*{transition:height .25s ease-in .5s}
.mistvideo-progress .bar{height:inherit;width:0;position:absolute;border-right:inherit;background-color:$accent;z-index:2}
.mistvideo-progress .buffer{height:inherit;width:0;position:absolute;background-color:$semiFill}
.mistvideo-progress .bar:after{content:'';border:5px solid $accent;border-radius:5px;position:absolute;right:-5px;top:50%;transform:translateY(-50%)}
.mistvideo-play[data-state=playing] svg.play{display:none}
.mistvideo-play[data-state=paused] svg.pause{display:none}
.mistvideo-main{align-items:center}
svg.icon.timeout{display:inline-block;height:1em;width:1em;margin:0;margin-right:.25em;vertical-align:top}
.mist.largeplay,.mist.muted{position:absolute;opacity:.5}
.mist.largeplay{top:50%;left:0;right:0;margin:auto;transform:translateY(-50%)}
.mist.muted{top:0;right:0;margin:1em}
.mistvideo-secondaryVideo{z-index:1;width:50%;height:50%}
.mistvideo-polling{display:inline-block;position:relative;width:25px;height:25px}
.mistvideo-polling svg.icon.loading{z-index:0;opacity:1}
.mistvideo[data-show-submenu] .mistvideo-submenu{right:5px}
.mistvideo[data-show-submenu] .mistvideo-controls{bottom:0}
.mistvideo-error[data-passive]{bottom:auto;left:auto;margin:.5em;padding:.5em}
.mistvideo-error[data-passive] .message{max-width:none}
.mistvideo-error .mistvideo-buttoncontainer{display:flex;flex-flow:row nowrap;justify-content:center}
.mistvideo-error .mistvideo-buttoncontainer .mistvideo-button{white-space:nowrap}
.browser-ie .mist.icon.loading{animation:spin 1.5s infinite linear;transform-origin:50% 50%}
.browser-ie .mist.icon.loading .spin{animation:none}
.mistvideo-log{margin:.5em 0}
.mistvideo-log .logs{max-height:10em;min-height:5em;width:100%;padding:.2em 0;padding-right:1em;overflow-y:auto;overflow-x:hidden;font-size:.9em}
.mistvideo-log .logs table td{vertical-align:top;padding:0}
.mistvideo-log .logs .entry .message{margin:0 .2em}
.mistvideo-log .logs .entry.type-error{color:$accent}
.mistvideo-log .logs .counter,.mistvideo-log .logs .timestamp{color:$semiFill}
.mistvideo-log .logs .timestamp:before{content:'['}
.mistvideo-log .logs .timestamp:after{content:']'}
.mistvideo-log .logs .counter:before{content:'('}
.mistvideo-log .logs .counter:after{content:'\00d7)'}
.mistvideo-devbuttons{font-size:.9em}
.mistvideo-devbuttons button{font-size:.8em;margin:.1em;padding:.2em .4em}
.mistvideo-forcePlayer>span,.mistvideo-forceSource>span,.mistvideo-forceType>span{display:inline-block;width:7em}
.mistvideo-forcePlayer>select,.mistvideo-forceSource>select,.mistvideo-forceType>select{width:15em;min-width:auto}
.mistvideo-devcontrols{margin:.5em 0}
.mistvideo-decodingIssues>*{display:flex;width:50%;max-width:20em;flex-flow:row nowrap;align-items:flex-end;justify-content:space-between}
.mistvideo-decodingIssues>*>:last-child{margin-right:.5em}
.mistvideo-decodingIssues{display:flex;flex-flow:row wrap;justify-content:space-between}
.mistvideo-decodingIssues label{position:relative}
.mistvideo-decodingIssues label .value{font-size:.8em}
svg.icon.graph{position:absolute;right:0;top:0;bottom:0;width:6em}
select{border-radius:0}
input[type=checkbox]{margin:0;margin-right:.2em;border:1px solid $semiFill;border-radius:0;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none;width:.8em;height:.8em;color:inherit;position:relative;cursor:pointer}
input[type=checkbox]:checked:after{content:"\2713";position:absolute;bottom:-.2em;left:0;font-size:1.2em}

View file

@ -0,0 +1 @@
mistplayers.dashjs={name:"Dash.js player",mimes:["dash/video/mp4"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(t){return MistUtil.array.indexOf(this.mimes,t)==-1?false:true},isBrowserSupported:function(t,e,i){if(location.protocol!=MistUtil.http.url.split(e.url).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}if(location.protocol=="file:"){i.log("This source ("+t+") won't load if the page is run via file://");return false}return"MediaSource"in window},player:function(){this.onreadylist=[]},scriptsrc:function(t){return t+"/dashjs.js"}};var p=mistplayers.dashjs.player;p.prototype=new MistPlayer;p.prototype.build=function(t,e){var i=this;this.onDashLoad=function(){if(t.destroyed){return}t.log("Building DashJS player..");var r=document.createElement("video");if("Proxy"in window){var a={get:{},set:{}};t.player.api=new Proxy(r,{get:function(t,e,i){if(e in a.get){return a.get[e].apply(t,arguments)}var r=t[e];if(typeof r==="function"){return function(){return r.apply(t,arguments)}}return r},set:function(t,e,i){if(e in a.set){return a.set[e].call(t,i)}return t[e]=i}});if(t.info.type=="live"){a.get.duration=function(){var e=0;if(this.buffered.length){e=this.buffered.end(this.buffered.length-1)}var i=((new Date).getTime()-t.player.api.lastProgress.getTime())*.001;return e+i+-1*t.player.api.liveOffset+45};a.set.currentTime=function(e){var i=e-t.player.api.duration;t.log("Seeking to "+MistUtil.format.time(e)+" ("+Math.round(i*-10)/10+"s from live)");t.video.currentTime=e};MistUtil.event.addListener(r,"progress",function(){t.player.api.lastProgress=new Date});t.player.api.lastProgress=new Date;t.player.api.liveOffset=0}}else{i.api=r}if(t.options.autoplay){r.setAttribute("autoplay","")}if(t.options.loop&&t.info.type!="live"){r.setAttribute("loop","")}if(t.options.poster){r.setAttribute("poster",t.options.poster)}if(t.options.controls=="stock"){r.setAttribute("controls","")}var s=dashjs.MediaPlayer().create();s.initialize(r,t.source.url,t.options.autoplay);i.dash=s;var o=["METRIC_ADDED","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(e){t.log("Player event fired: "+e.type)})}}t.player.setSize=function(t){this.api.style.width=t.width+"px";this.api.style.height=t.height+"px"};t.player.api.setSource=function(e){t.player.dash.attachSource(e)};t.player.api.setTrack=function(e,r){var a=MistUtil.tracks.parse(t.info.meta.tracks);if(!(e in a)||!(r in a[e])&&r!=0){t.log("Skipping trackselection of "+e+" track "+r+" because it does not exist");return}var s=i.dash.getBitrateInfoListFor("video").length-1;for(var o in t.info.meta.tracks){var n=t.info.meta.tracks[o];if(n.type==e){if(n.trackid==r){break}s--}}i.dash.setAutoSwitchQualityFor(e,false);i.dash.setFastSwitchEnabled(true);i.dash.setQualityFor(e,s)};i.dash.on("qualityChangeRendered",function(e){var r=i.dash.getBitrateInfoListFor("video").length-1;var a;for(var s in t.info.meta.tracks){var o=t.info.meta.tracks[s];if(o.type==e.mediaType){if(e.newQuality==r){a=o.trackid;break}r--}}MistUtil.event.send("playerUpdate_trackChanged",{type:e.mediaType,trackid:a},t.video)});MistUtil.event.addListener(r,"progress",function(e){if(t.container.getAttribute("data-loading")=="stalled"){t.container.removeAttribute("data-loading")}});i.api.unload=function(){i.dash.reset()};t.log("Built html");e(r)};if("dashjs"in window){this.onDashLoad()}else{var r=MistUtil.scripts.insert(t.urlappend(mistplayers.dashjs.scriptsrc(t.options.host)),{onerror:function(e){var i="Failed to load dashjs.js";if(e.message){i+=": "+e.message}t.showError(i)},onload:i.onDashLoad},t)}};

View file

@ -0,0 +1 @@
mistplayers.flash_strobe={name:"Strobe Flash media playback",mimes:["flash/10","flash/11","flash/7"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(t){return this.mimes.indexOf(t)==-1?false:true},isBrowserSupported:function(t,e,i){if(MistUtil.http.url.split(e.url).protocol.slice(0,4)=="http"&&location.protocol!=MistUtil.http.url.split(e.url).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}var r=0;try{var a=navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin;if(a.version){r=a.version.split(".")[0]}else{r=a.description.replace(/([^0-9\.])/g,"").split(".")[0]}}catch(t){}try{r=new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/([^0-9\,])/g,"").split(",")[0]}catch(t){}if(!r){return false}var l=t.split("/");return Number(r)>=Number(l[l.length-1])},player:function(){this.onreadylist=[]}};var p=mistplayers.flash_strobe.player;p.prototype=new MistPlayer;p.prototype.build=function(t,e){var i=document.createElement("object");var r=document.createElement("embed");i.appendChild(r);function a(e){var a=t.options;function l(t,e){var i=document.createElement("param");i.setAttribute("name",t);i.setAttribute("value",e);return i}MistUtil.empty(i);i.appendChild(l("movie",t.urlappend(a.host+t.source.player_url)));var s="src="+encodeURIComponent(e)+"&controlBarMode="+(a.controls?"floating":"none")+"&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3"+(a.live?"&streamType=live":"")+(a.autoplay?"&autoPlay=true":"");i.appendChild(l("flashvars",s));i.appendChild(l("allowFullScreen","true"));i.appendChild(l("wmode","direct"));if(a.autoplay){i.appendChild(l("autoPlay","true"))}r.setAttribute("src",t.urlappend(t.source.player_url));r.setAttribute("type","application/x-shockwave-flash");r.setAttribute("allowfullscreen","true");r.setAttribute("flashvars",s)}a(t.source.url);this.api={};this.setSize=function(t){i.setAttribute("width",t.width);i.setAttribute("height",t.height);r.setAttribute("width",t.width);r.setAttribute("height",t.height)};this.setSize(t.calcSize());this.onready(function(){if(t.container){t.container.removeAttribute("data-loading")}});this.api.setSource=function(t){a(t)};t.log("Built html");e(i)};

View file

@ -0,0 +1 @@
mistplayers.html5={name:"HTML5 video player",mimes:["html5/application/vnd.apple.mpegurl","html5/video/mp4","html5/video/ogg","html5/video/webm","html5/audio/mp3","html5/audio/webm","html5/audio/ogg","html5/audio/wav"],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){if(location.protocol=="file:"&&MistUtil.http.url.split(t.url).protocol=="http:"){i.log("This page was loaded over file://, the player might not behave as intended.")}else{i.log("HTTP/HTTPS mismatch for this source");return false}}var r=false;var a=e.split("/");a.shift();try{a=a.join("/");function n(e){var t=document.createElement("video");if(t&&t.canPlayType(e)!=""){r=t.canPlayType(e)}return r}if(a=="video/mp4"){function o(e){function t(t){return("0"+e.init.charCodeAt(t).toString(16)).slice(-2)}switch(e.codec){case"AAC":return"mp4a.40.2";case"MP3":return"mp3";case"AC3":return"ec-3";case"H264":return"avc1."+t(1)+t(2)+t(3);case"HEVC":return"hev1."+t(1)+t(6)+t(7)+t(8)+t(9)+t(10)+t(11)+t(12);default:return e.codec.toLowerCase()}}var s={};for(var l in i.info.meta.tracks){if(i.info.meta.tracks[l].type!="meta"){s[o(i.info.meta.tracks[l])]=1}}s=MistUtil.object.keys(s);if(s.length){if(s.length>t.simul_tracks){var p=0;for(var l in s){var u=n(a+';codecs="'+s[l]+'"');if(u){p++}}return p>=t.simul_tracks}a+=';codecs="'+s.join(",")+'"'}}r=n(a)}catch(e){}return r},player:function(){this.onreadylist=[]},mistControls:true};var p=mistplayers.html5.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var i=e.source.type.split("/");i.shift();var r=document.createElement("video");r.setAttribute("crossorigin","anonymous");var a=document.createElement("source");a.setAttribute("src",e.source.url);r.source=a;r.appendChild(a);a.type=i.join("/");var n=["autoplay","loop","poster"];for(var o in n){var s=n[o];if(e.options[s]){r.setAttribute(s,e.options[s]===true?"":e.options[s])}}if(e.options.controls=="stock"){r.setAttribute("controls","")}if(e.info.type=="live"){r.loop=false}if("Proxy"in window&&"Reflect"in window){var l={get:{},set:{}};e.player.api=new Proxy(r,{get:function(e,t,i){if(t in l.get){return l.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 l.set){return l.set[t].call(e,i)}return e[t]=i}});if(e.source.type=="html5/audio/mp3"){l.set.currentTime=function(){e.log("Seek attempted, but MistServer does not currently support seeking in MP3.");return false}}if(e.info.type=="live"){l.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-e.player.api.liveOffset};l.set.currentTime=function(t){var i=t-e.player.api.duration;e.player.api.liveOffset=i;e.log("Seeking to "+MistUtil.format.time(t)+" ("+Math.round(i*-10)/10+"s from live)");e.player.api.setSource(MistUtil.http.url.addParam(e.source.url,{startunix:i}))};MistUtil.event.addListener(r,"progress",function(){e.player.api.lastProgress=new Date});e.player.api.lastProgress=new Date;e.player.api.liveOffset=0;MistUtil.event.addListener(r,"pause",function(){e.player.api.pausedAt=new Date});l.get.play=function(){return function(){if(e.player.api.paused&&e.player.api.pausedAt&&new Date-e.player.api.pausedAt>5e3){r.load();e.log("Reloading source..")}return r.play.apply(r,arguments)}};if(e.source.type=="html5/video/mp4"){l.get.currentTime=function(){return this.currentTime-e.player.api.liveOffset+e.info.lastms*.001}}}else{if(!isFinite(r.duration)){var p=0;for(var o in e.info.meta.tracks){p=Math.max(p,e.info.meta.tracks[o].lastms)}l.get.duration=function(){if(isFinite(this.duration)){return this.duration}return p*.001}}}}else{e.player.api=r}e.player.api.setSource=function(e){if(e!=this.source.src){this.source.src=e;this.load()}};e.player.api.setSubtitle=function(e){var t=r.getElementsByTagName("track");for(var i=t.length-1;i>=0;i--){r.removeChild(t[i])}if(e){var a=document.createElement("track");r.appendChild(a);a.kind="subtitles";a.label=e.label;a.srclang=e.lang;a.src=e.src;a.setAttribute("default","")}};e.player.setSize=function(e){this.api.style.width=e.width+"px";this.api.style.height=e.height+"px"};t(r)};

View file

@ -0,0 +1 @@
mistplayers.videojs={name:"VideoJS player",mimes:["html5/application/vnd.apple.mpegurl"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return this.mimes.indexOf(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:"&&e=="html5/application/vnd.apple"){i.log("This source ("+e+") won't load if the page is run via file://");return false}return"MediaSource"in window},player:function(){},scriptsrc:function(e){return e+"/videojs.js"}};var p=mistplayers.videojs.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var i=this;function r(){if(e.destroyed){return}e.log("Building VideoJS player..");var r=document.createElement("video");if(e.source.type!="html5/video/ogg"){r.crossOrigin="anonymous"}var o=e.source.type.split("/");o.shift();var n=document.createElement("source");n.setAttribute("src",e.source.url);i.source=n;r.appendChild(n);n.type=o.join("/");e.log("Adding "+n.type+" source @ "+e.source.url);if(n.type=="application/vnd.apple.mpegurl"){n.type="application/x-mpegURL"}MistUtil.class.add(r,"video-js");var s={};if(e.options.autoplay){s.autoplay=true}if(e.options.loop&&e.info.type!="live"){s.loop=true;r.loop=true}if(e.options.poster){s.poster=e.options.poster}if(e.options.controls=="stock"){r.setAttribute("controls","");if(!document.getElementById("videojs-css")){var l=document.createElement("link");l.rel="stylesheet";l.href=e.options.host+"/skins/videojs.css";l.id="videojs-css";document.head.appendChild(l)}}i.onready(function(){i.videojs=videojs(r,s,function(){e.log("Videojs initialized")});i.api.unload=function(){videojs(r).dispose()}});e.log("Built html");if("Proxy"in window&&"Reflect"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"){function p(e){var t=0;if(e.buffered.length){t=e.buffered.end(e.buffered.length-1)}return t}var u=90;a.get.duration=function(){return(e.info.lastms+(new Date).getTime()-e.info.updated.getTime())*.001};e.player.api.lastProgress=new Date;e.player.api.liveOffset=0;MistUtil.event.addListener(r,"progress",function(){e.player.api.lastProgress=new Date});a.set.currentTime=function(t){var i=e.player.api.currentTime-t;var r=t-e.player.api.duration;e.log("Seeking to "+MistUtil.format.time(t)+" ("+Math.round(r*-10)/10+"s from live)");e.video.currentTime-=i};a.get.currentTime=function(){return this.currentTime+e.info.lastms*.001-e.player.api.liveOffset-u}}}else{i.api=r}e.player.setSize=function(t){if("videojs"in e.player){e.player.videojs.dimensions(t.width,t.height);r.parentNode.style.width=t.width+"px";r.parentNode.style.height=t.height+"px"}this.api.style.width=t.width+"px";this.api.style.height=t.height+"px"};e.player.api.setSource=function(t){if(!e.player.videojs){return}if(e.player.videojs.src()!=t){e.player.videojs.src({type:e.player.videojs.currentSource().type,src:t})}};e.player.api.setSubtitle=function(e){var t=r.getElementsByTagName("track");for(var i=t.length-1;i>=0;i--){r.removeChild(t[i])}if(e){var o=document.createElement("track");r.appendChild(o);o.kind="subtitles";o.label=e.label;o.srclang=e.lang;o.src=e.src;o.setAttribute("default","")}};t(r)}if("videojs"in window){r()}else{var o=MistUtil.scripts.insert(e.urlappend(mistplayers.videojs.scriptsrc(e.options.host)),{onerror:function(t){var i="Failed to load videojs.js";if(t.message){i+=": "+t.message}e.showError(i)},onload:r},e)}};

File diff suppressed because one or more lines are too long

48
embed/minimize.sh Executable file
View file

@ -0,0 +1,48 @@
#!/bin/bash
echo "Minimizing player code..";
echo " Minimizing JS..";
if [ "min/player.js" -ot "util.js" ] || [ "min/player.js" -ot "skins.js" ] || [ "min/player.js" -ot "controls.js" ] || [ "min/player.js" -ot "player.js" ]; then
echo " Minimizing 'util.js skins.js controls.js player.js' into 'min/player.js'..";
terser -mc -o min/player.js -- util.js skins.js controls.js player.js
fi
echo " Done.";
echo " Minimizing wrappers.."
if [ "min/wrappers/dashjs.js" -ot "wrappers/dashjs.js" ]; then
echo " Minimizing dashjs";
terser -mn -o min/wrappers/dashjs.js -- wrappers/dashjs.js
fi
if [ "min/wrappers/flash_strobe.js" -ot "wrappers/flash_strobe.js" ]; then
echo " Minimizing flash_strobe";
terser -mn -o min/wrappers/flash_strobe.js -- wrappers/flash_strobe.js
fi
if [ "min/wrappers/html5.js" -ot "wrappers/html5.js" ]; then
echo " Minimizing html5";
terser -mn -o min/wrappers/html5.js -- wrappers/html5.js
fi
if [ "min/wrappers/videojs.js" -ot "wrappers/videojs.js" ]; then
echo " Minimizing videojs";
terser -mn -o min/wrappers/videojs.js -- wrappers/videojs.js
fi
if [ "min/wrappers/webrtc.js" -ot "wrappers/webrtc.js" ]; then
echo " Minimizing webrtc";
terser -mn -o min/wrappers/webrtc.js -- wrappers/webrtc.js
fi
echo " Done.";
echo " Minimizing CSS..";
if [ "min/skins/default.css" -ot "skins/default.css" ] || [ "min/skins/default.css" -ot "skins/general.css" ]; then
echo " Minimizing default";
cleancss --format keep-breaks -o min/skins/default.css skins/general.css skins/default.css
fi
if [ "min/skins/dev.css" -ot "skins/default.css" ] || [ "min/skins/dev.css" -ot "skins/general.css" ] || [ "min/skins/dev.css" -ot "skins/dev.css" ]; then
echo " Minimizing dev";
cleancss --format keep-breaks -o min/skins/dev.css skins/general.css skins/default.css skins/dev.css
fi
echo " Done.";
echo "Done.";

File diff suppressed because one or more lines are too long

1127
embed/player.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

8
embed/players/update.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
echo "Dashjs"
wget https://cdn.dashjs.org/latest/dash.all.min.js -O dash.all.min.js
echo "Videojs"
echo "You'll want to check for the latest version at https://videojs.com/getting-started/#download-cdn"
wget https://vjs.zencdn.net/7.3.0/video.min.js -O video.min.js

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

1
embed/players/webrtc.js Normal file

File diff suppressed because one or more lines are too long

2559
embed/skins.js Normal file

File diff suppressed because it is too large Load diff

131
embed/skins/default.css Normal file
View file

@ -0,0 +1,131 @@
.mistvideo {
line-height: 1.2;
font-size: 14.5px;
}
.mistvideo svg {
margin: 2.5px;
}
.mistvideo-background { background-color: $background; }
.mistvideo-totalTime:before {
content: '/';
margin: 0.2em;
}
.mistvideo-progress {
padding: 10px 0;
margin: -10px 0;
z-index: 2; /*keep above control bar*/
}
.mistvideo-progress > * {
height: 2px;
background-color: $progressBackground;
opacity: 0.95;
position: relative;
}
.mistvideo-novideo .mistvideo-progress > *,
.mistvideo-progress:hover > * {
height: 10px;
}
.mistvideo-progress:not(:hover) > * {
transition: height 0.25s ease-in 0.5s;
}
.mistvideo-progress .bar {
height: inherit;
width: 0;
position: absolute;
border-right: inherit;
background-color: $accent;
z-index: 2; /*keep above buffer bar(s)*/
}
.mistvideo-progress .buffer {
height: inherit;
width: 0;
position: absolute;
background-color: $semiFill;
}
.mistvideo-progress .bar:after {
content: '';
border: 5px solid $accent;
border-radius: 5px;
position: absolute;
right: -5px;
top: 50%;
transform: translateY(-50%);
}
.mistvideo-play[data-state="playing"] svg.play {
display: none;
}
.mistvideo-play[data-state="paused"] svg.pause {
display: none;
}
.mistvideo-main { align-items: center; }
svg.icon.timeout {
display: inline-block;
height: 1em;
width: 1em;
margin: 0;
margin-right: 0.25em;
vertical-align: top;
}
.mist.largeplay, .mist.muted {
position: absolute;
opacity: 0.5;
}
.mist.largeplay {
top: 50%;
left: 0;
right: 0;
margin: auto;
transform: translateY(-50%);
}
.mist.muted {
top: 0;
right: 0;
margin: 1em;
}
.mistvideo-secondaryVideo {
z-index: 1;
width: 50%;
height: 50%;
}
.mistvideo-polling {
display: inline-block;
position: relative;
width: 25px;
height: 25px;
}
.mistvideo-polling svg.icon.loading {
z-index: 0;
opacity: 1;
}
.mistvideo[data-show-submenu] .mistvideo-submenu {
right: 5px;
}
.mistvideo[data-show-submenu] .mistvideo-controls {
bottom: 0;
}
.mistvideo-error[data-passive] {
bottom: auto;
left: auto;
margin: 0.5em;
padding: 0.5em;
}
.mistvideo-error[data-passive] .message {
max-width: none;
}
.mistvideo-error .mistvideo-buttoncontainer {
display: flex;
flex-flow: row nowrap;
justify-content: center;
}
.mistvideo-error .mistvideo-buttoncontainer .mistvideo-button {
white-space: nowrap;
}
.browser-ie .mist.icon.loading {
animation: spin 1.5s infinite linear;
transform-origin: 50% 50%;
}
.browser-ie .mist.icon.loading .spin { animation: none; }

105
embed/skins/dev.css Normal file
View file

@ -0,0 +1,105 @@
.mistvideo-log {
margin: 0.5em 0;
}
.mistvideo-log .logs {
max-height: 10em;
min-height: 5em;
width: 100%;
padding: 0.2em 0;
padding-right: 1em; /*for scrollbar*/
overflow-y: auto;
overflow-x: hidden;
font-size: 0.9em;
}
.mistvideo-log .logs table td {
vertical-align: top;
padding: 0;
}
.mistvideo-log .logs .entry .message {
margin: 0 0.2em;
}
.mistvideo-log .logs .entry.type-error {
color: $accent;
}
.mistvideo-log .logs .timestamp,
.mistvideo-log .logs .counter {
color: $semiFill;
}
.mistvideo-log .logs .timestamp:before { content: '['; }
.mistvideo-log .logs .timestamp:after { content: ']'; }
.mistvideo-log .logs .counter:before { content: '('; }
.mistvideo-log .logs .counter:after { content: '\00d7)'; }
.mistvideo-devbuttons { font-size: 0.9em; }
.mistvideo-devbuttons button {
font-size: 0.8em;
margin: 0.1em;
padding: 0.2em 0.4em;
}
.mistvideo-forceSource > span,
.mistvideo-forceType > span,
.mistvideo-forcePlayer > span { display: inline-block; width: 7em; }
.mistvideo-forceSource > select,
.mistvideo-forceType > select,
.mistvideo-forcePlayer > select {
width: 15em;
min-width: auto;
}
.mistvideo-devcontrols {
margin: 0.5em 0;
}
.mistvideo-decodingIssues > * {
display: flex;
width: 50%;
max-width: 20em;
flex-flow: row nowrap;
align-items: flex-end;
justify-content: space-between;
}
.mistvideo-decodingIssues > * > :last-child {
margin-right: 0.5em;
}
.mistvideo-decodingIssues {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
.mistvideo-decodingIssues label {
position: relative;
}
.mistvideo-decodingIssues label .value {
font-size: 0.8em;
}
svg.icon.graph {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 6em;
}
select {
border-radius: 0;
}
input[type="checkbox"] {
margin: 0;
margin-right: 0.2em;
border: 1px solid $semiFill;
border-radius: 0;
-webkit-appearance: none;
-moz-appearance: none;
-ms-appearance: none;
appearance: none;
width: 0.8em;
height: 0.8em;
color: inherit;
position: relative;
cursor: pointer;
}
input[type="checkbox"]:checked:after {
content: "\2713";
position: absolute;
bottom: -0.2em;
left: 0;
font-size: 1.2em;
}

224
embed/skins/general.css Normal file
View file

@ -0,0 +1,224 @@
.mistvideo {
display: inline-block;
position: relative;
color: $stroke;
font-family: sans-serif;
background-color: #000;
justify-content: center;
align-items: center;
}
.mistvideo-controls {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.mistvideo.novideo {
overflow: visible;
}
.mistvideo-video {
overflow: hidden;
}
svg.icon.loading {
z-index: -1; /* don't use display: none because of transition for [data-loading=stalled] */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
opacity: 0;
}
[data-loading] {
position: relative;
}
/*wait before showing icon when stalled*/
[data-loading="stalled"] svg.icon.loading {
transition: opacity 0s 3s;
}
[data-loading] svg.icon.loading {
z-index: 2;
opacity: 1;
}
[data-loading-css] .mistvideo-controls {
display: none;
}
[data-hidecursor],
[data-hidecursor] .mistvideo-pointer,
[data-hidecursor] *
{ cursor: none }
.mistvideo-error {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: $background;
align-items: center;
justify-content: center;
text-align: center;
z-index: 2; /*above progress bar*/
cursor: default; /*cursor won't show up if it was hidden because the mousemove event can no longer reach the video element*/
min-height: fit-content; /*overflow if needed*/
min-width: fit-content;
}
.mistvideo-error.show { display: flex; }
.mistvideo-error .message { max-width: 80%; }
.mistvideo-error .message .details table { text-align: left; }
.mistvideo-video:not(.video-js) button,
.mistvideo-error button,
.mistvideo-controls button
{
color: $stroke;
border: 1px solid $semiFill;
background-color: $background;
margin: 0.25em;
padding: 0.5em 1em;
opacity: 0.5;
cursor: pointer;
}
button:hover { opacity: 1; }
select {
background-color: transparent;
color: $stroke;
border: none;
margin: 0 0.5em;
font-size: inherit;
cursor: pointer;
-ms-background-color: red;
}
select > option {
background-color: $background;
}
.browser-edge select, .browser-safari select {
/*These browsers don't show an arrow for select boxes, causing them to be invisible when a border is not applied */
border: 1px solid $semiFill;
border-top: none;
border-left: none;
margin-top: 2px;
}
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
[data-fullscreen] {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100% !important;
height: 100% !important;
}
video {
display: block;
flex-shrink: 0;
}
table { color: inherit; font-size: inherit; font-style: inherit; }
audio:not([controls]) { display: block !important; }
.mistvideo-padding { padding: 5px 10px; }
.mistvideo-pointer { cursor: pointer; }
.description {
color: $semiFill;
font-size: 0.9em;
}
.mistvideo-container {
display: flex;
flex-wrap: nowrap;
}
.mistvideo-container.mistvideo { display: inline-flex; max-width: 100%; }
.mistvideo-container.mistvideo-row { flex-direction: row; }
.mistvideo-container.mistvideo-column { flex-direction: column; }
.mistvideo-container.mistvideo-center { justify-content: center; }
.mistvideo-align-right {
margin-left: auto;
margin-right: 0;
}
.hover_window_container.pos > .outer_window {
position: absolute;
pointer-events: none;
overflow: hidden;
}
.hover_window_container.pos > .outer_window > .inner_window {
position: absolute;
pointer-events: all;
}
.hover_window_container:not(:hover) > .outer_window > .inner_window,
.hover_window_container > .outer_window[data-hidecursor] > .inner_window
{ transition: all 0.5s ease-in 0.5s; }
.outer_window > .inner_window { flex-grow: 1; }
.hover_window_container > .outer_window > .inner_window > * { flex-shrink: 0; }
.mistvideo-draggable {
overflow: hidden;
}
.mistvideo-tooltip {
position: absolute;
background-color: $background;
padding: 0.5em 1em;
z-index: 2;
}
*:not(:hover) > .mistvideo-tooltip { transition: opacity 0.25s ease-in; }
.mistvideo-tooltip .triangle {
border: 10px solid $background;
position: absolute;
}
.mistvideo-tracks label {
display: block;
}
.mistvideo-tracks label > span {
margin-right: 1em;
}
a {
color: $accent;
}
.mistvideo-log .logs {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.mistvideo-placeholder {
max-width: 100%;
max-height: 100%;
}
.mistvideo-topright {
position: absolute;
top: 0;
right: 0;
}
.mistvideo-topleft {
position: absolute;
top: 0;
left: 0;
}
.mistvideo-delay-display {
animation: appear 1s;
animation-iteration-count: 1;
animation-timing-function: steps(1,end);
}
@keyframes appear { from { opacity: 0; } to { opacity: 1; } }
svg.icon {
display: block;
stroke-width: $strokeWidth;
fill: none;
stroke: none;
}
svg.icon.fill, svg.icon .fill { fill: $fill; }
svg.icon.semiFill, svg.icon .semiFill { fill: $semiFill; }
svg.icon.stroke, svg.icon .stroke { stroke: $stroke; vector-effect: non-scaling-stroke; }
svg.icon.off .toggle.fill,
svg.icon.off .toggle.semiFill,
svg.icon.off .toggle .fill,
svg.icon.off .toggle .semiFill { fill: none; }
svg.icon.spin, svg.icon .spin {
animation: spin 1.5s infinite linear;
transform-origin: 50% 50%;
}

1279
embed/skins/video-js.css Normal file

File diff suppressed because one or more lines are too long

954
embed/util.js Normal file
View file

@ -0,0 +1,954 @@
var MistUtil = {
format: {
time: function(secs,options){
if (isNaN(secs) || !isFinite(secs)) { return secs; }
if (!options) { options = {}; }
var ago = (secs < 0 ? " ago" : "");
secs = Math.abs(secs);
var days = Math.floor(secs / 86400)
secs = secs - days * 86400;
var hours = Math.floor(secs / 3600);
secs = secs - hours * 3600;
var mins = Math.floor(secs / 60);
var ms = Math.round((secs % 1)*1e3);
secs = Math.floor(secs - mins * 60);
var str = [];
if (days) {
days = days+" day"+(days > 1 ? "s" : "")+", ";
}
if ((hours) || (days)) {
str.push(hours);
str.push(("0"+mins).slice(-2));
}
else {
str.push(mins); //don't use 0 padding if there are no hours in front
}
str.push(("0"+Math.floor(secs)).slice(-2));
if (options.ms) {
str[str.length-1] += "."+("000"+ms).slice(-3);
}
return (days ? days : "")+str.join(":")+ago;
},
ucFirst: function(string){
return string.charAt(0).toUpperCase()+string.slice(1);
},
number: function(num) {
if ((isNaN(Number(num))) || (num == 0)) { return num; }
//rounding
//use a significance of three, but don't round "visible" digits
var sig = Math.max(3,Math.ceil(Math.log(num)/Math.LN10));
var mult = Math.pow(10,sig - Math.floor(Math.log(num)/Math.LN10) - 1);
num = Math.round(num * mult) / mult;
//thousand seperation
if (num >= 1e4) {
var seperator = "";
number = num.toString().split(".");
var regex = /(\d+)(\d{3})/;
while (regex.test(number[0])) {
number[0] = number[0].replace(regex,"$1"+seperator+"$2");
}
num = number.join(".");
}
return num;
},
bytes: function(val){
if (isNaN(Number(val))) { return val; }
var suffix = ["bytes","KB","MB","GB","TB","PB"];
if (val == 0) {
unit = suffix[0];
}
else {
var exponent = Math.floor(Math.log(Math.abs(val)) / Math.log(1024));
if (exponent < 0) {
unit = suffix[0];
}
else {
val = val / Math.pow(1024,exponent);
unit = suffix[exponent];
}
}
return this.number(val)+unit;
},
mime2human: function(mime){
switch (mime) {
case "html5/video/webm": {
return "WebM";
break;
}
case "html5/application/vnd.apple.mpegurl": {
return "HLS";
break;
}
case "flash/10": {
return "Flash (RTMP)";
break;
}
case "flash/11": {
return "Flash (HDS)";
break;
}
case "flash/7": {
return "Flash (Progressive)";
break;
}
case "html5/video/mpeg": {
return "TS";
break;
}
case "html5/application/vnd.ms-ss": {
return "Smooth streaming";
break;
}
case "dash/video/mp4": {
return "DASH";
break;
}
case "webrtc": {
return "WebRTC";
break;
}
case "silverlight": {
return "Smooth streaming (Silverlight)";
break;
}
case "html5/text/vtt": {
return "VTT subtitles";
break;
}
case "html5/text/plain": {
return "SRT subtitles";
break;
}
default: {
return mime.replace("html5/","").replace("video/","").replace("audio/","").toLocaleUpperCase();
}
}
}
},
class: {
//reroute classList functionalities if not supported; also avoid indexOf
add: function(DOMelement,item){
if ("classList" in DOMelement) {
DOMelement.classList.add(item);
}
else {
var classes = this.get(DOMelement);
classes.push(item);
this.set(DOMelement,classes);
}
},
remove: function(DOMelement,item){
if ("classList" in DOMelement) {
DOMelement.classList.remove(item);
}
else {
var classes = this.get(DOMelement);
for (var i = classes.length-1; i >= 0; i--) {
if (classes[i] == item) {
classes.splice(i);
}
}
this.set(DOMelement,classes);
}
},
get: function(DOMelement) {
var classes;
var className = DOMelement.getAttribute("class"); //DOMelement.className does not work on svg elements
if ((!className) || (className == "")) { classes = []; }
else { classes = className.split(" "); }
return classes;
},
set: function(DOMelement,classes) {
DOMelement.setAttribute("class",classes.join(" "));
},
has: function(DOMelement,hasClass){
return (DOMelement.className.split(" ").indexOf(hasClass) >= 0)
}
},
object: {
//extend object1 with object2
extend: function(object1,object2,deep) {
for (var i in object2) {
if (deep && (typeof object2[i] == "object") && (!("nodeType" in object2[i]))) {
if (!(i in object1)) {
if (MistUtil.array.is(object2[i])) {
object1[i] = [];
}
else {
object1[i] = {};
}
}
this.extend(object1[i],object2[i],true);
}
else {
object1[i] = object2[i];
}
}
return object1;
},
//replace Object.keys
//if sorting: sort the keys alphabetically or use passed sorting function
//sorting gets these arguments: keya,keyb,valuea,valueb
keys: function(obj,sorting){
var keys = [];
for (var i in obj) {
keys.push(i);
}
if (sorting) {
if (typeof sorting != "function") {
sorting = function(keya,keyb){
if (keya > keyb) { return 1; }
if (keya < keyb) { return -1; }
return 0;
};
}
keys.sort(function(keya,keyb){
return sorting(keya,keyb,obj[keya],obj[keyb]);
});
}
return keys;
},
//replace Object.values
//if sorting: sort the keys alphabetically or use passed sorting function
//sorting gets these arguments: keya,keyb,valuea,valueb
values: function(obj,sorting){
var keys = this.keys(obj,sorting);
values = [];
for (var i in keys) {
values.push(obj[keys[i]]);
}
return values;
}
},
array: {
//replace [].indexOf
indexOf: function(array,entry) {
if (!(array instanceof Array)) { throw "Tried to use indexOf on something that is not an array"; }
if ("indexOf" in array) { return array.indexOf(entry); }
for (var i; i < array.length; i++) {
if (array[i] == entry) {
return i;
}
}
return -1;
},
//replace isArray
is: function(array) {
if ("isArray" in Array) {
return Array.isArray(array);
}
return Object.prototype.toString.call(array) === '[object Array]';
},
multiSort: function (array,sortby) {
/*
MistUtil.array.multiSort([].concat(video.info.source),[
{type: ["html5/video/webm","silverlight"]} or ["type",["html5/video/webm","silverlight"]]
,{simul_tracks:-1} or ["simul_tracks",-1]
,function(a){ return a.priority * -1; }
,"url"
]);
*/
var sortfunc = function(a,b){
if (isNaN(a) || isNaN(b)) {
return a.localeCompare(b);
}
return Math.sign(a-b);
};
if (!sortby.length) { return array.sort(sortfunc); }
function getValue(key,a) {
function parseIt(item,key,sortvalue){
if (!(key in item)) {
throw "Invalid sorting rule: "+JSON.stringify([key,sortvalue])+". \""+key+"\" is not a key of "+JSON.stringify(item);
}
if (typeof sortvalue == "number") {
//deals with something like {priority: -1}
if (key in item) {
return item[key] * sortvalue;
}
}
//deals with something like {type:["webrtc"]}
var i = sortvalue.indexOf(item[key])
return (i >= 0 ? i : sortvalue.length);
}
//deals with something like function(a){ return a.foo + a.bar; }
if (typeof key == "function") { return key(a); }
if (typeof key == "object") {
if (key instanceof Array) {
//it's an array
return parseIt(a,key[0],key[1]);
}
//it's an object
for (var j in key) { //only listen to a single key
return parseIt(a,j,key[j]);
}
}
if (key in a) {
return a[key];
}
throw "Invalid sorting rule: "+key+". This should be a function, object or key of "+JSON.stringify(a)+".";
}
array.sort(function(a,b){
var output = 0;
for (var i in sortby) {
var key = sortby[i];
output = sortfunc(getValue(key,a),getValue(key,b));
if (output != 0) {
break;
}
}
return output;
});
return array;
}
},
createUnique: function() {
var i = "uid"+Math.random().toString().replace("0.","");
if (document.querySelector("."+i)) {
//if this is already used, try again
return createUnique();
}
return i;
},
http: {
getpost: function(type,url,data,callback,errorCallback) {
var xhr = new XMLHttpRequest();
xhr.open(type, url, true);
if (type == "POST") { xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); }
if (errorCallback) { xhr.timeout = 8e3; } //go to timeout function after 8 seconds
xhr.onload = function() {
var status = xhr.status;
if ((status >= 200) && (status < 300)) {
callback(xhr.response);
}
else if (errorCallback) {
xhr.onerror = function() {
errorCallback(xhr);
}
}
};
if (errorCallback) {
xhr.onerror = function() {
errorCallback(xhr);
}
xhr.ontimeout = xhr.onerror;
}
if (type == "POST") {
var poststr;
var post = [];
for (var i in data) {
post.push(i+"="+encodeURIComponent(data[i]));
}
if (post.length) { poststr = post.join("&"); }
xhr.send(poststr);
}
else {
xhr.send();
}
},
get: function(url,callback,errorCallback){
this.getpost("GET",url,null,callback,errorCallback);
},
post: function(url,data,callback,errorCallback){
this.getpost("POST",url,data,callback,errorCallback);
},
url: {
addParam: function(url,params){
var spliturl = url.split("?");
var ret = [spliturl.shift()];
var splitparams = [];
if (spliturl.length) {
splitparams = spliturl[0].split("&");
}
for (var i in params) {
splitparams.push(i+"="+params[i]);
}
if (splitparams.length) { ret.push(splitparams.join("&")); }
return ret.join("?");
},
split: function(url){
var a = document.createElement("a");
a.href = url;
return {
protocol: a.protocol,
host: a.hostname,
hash: a.hash,
port: a.port,
path: a.pathname.replace(/\/*$/,"")
};
},
sanitizeHost: function(host){
var split = MistUtil.http.url.split(host);
return split.protocol + "//" + split.host + (split.port && (split.port != "") ? ":"+split.port : "") + (split.hash && (split.hash != "") ? "#"+split.hash : "") + (split.path ? split.path : "");
}
}
},
css: {
cache: {},
load: function(url,colors,callback){
var style = document.createElement("style");
style.type = "text/css";
style.setAttribute("data-source",url);
if (callback) { style.callback = callback; }
var cache = this.cache;
function onCSSLoad(d) {
//parse rules and replace variables; expected syntax $abc[.abc]
var css = MistUtil.css.applyColors(d,colors);
if ("callback" in style) { style.callback(css); }
else { style.textContent = css; }
}
if (url in cache) {
if (cache[url] instanceof Array) {
cache[url].push(onCSSLoad);
}
else {
onCSSLoad(cache[url]);
}
}
else {
//retrieve file contents
cache[url] = [onCSSLoad];
MistUtil.http.get(url,function(d){
for (var i in cache[url]) {
cache[url][i](d);
}
cache[url] = d;
},function(){
throw "Failed to load CSS from "+url;
});
}
return style; //its empty now, but will be filled on load
},
applyColors: function(css,colors) {
return css.replace(/\$([^\s^;^}]*)/g,function(str,variable){
var index = variable.split(".");
var val = colors;
for (var j in index) {
val = val[index[j]];
}
return val;
});
},
createStyle: function(css,prepend,applyToChildren){
var style = document.createElement("style");
style.type = "text/css";
if (css) {
if (prepend) {
css = this.prependClass(css,prepend,applyToChildren);
}
style.textContent = css;
}
return style;
},
prependClass: function (css,prepend,applyToChildren) {
var style = false;
if (typeof css != "string") {
style = css;
if (!("unprepended" in style)) {
style.unprepended = style.textContent;
}
css = style.unprepended;
}
//remove all block comments
css = css.replace(/\/\*.*?\*\//g,"");
//save all @{} blocks
var save = css.match(/@.*?{.*}/g);
for (var i in save) {
css = css.replace(save[i],"@@@@");
}
//find and replace selectors
css = css.replace(/[^@]*?{[^]*?}/g,function(match){
var split = match.split("{")
var selectors = split[0].split(",");
var properties = "{"+split.slice(1).join("}");
for (var i in selectors) {
selectors[i] = selectors[i].trim();
var str = "."+prepend+selectors[i];
if (applyToChildren) {
str += ",\n."+prepend+" "+selectors[i];
}
selectors[i] = str;
}
return "\n"+selectors+" "+properties;
});
//reinsert saved blocks
for (var i in save) {
css = css.replace(/@@@@/,save[i]);
}
if (style) {
style.textContent = css;
return;
}
return css;
}
},
empty: function(DOMelement) {
while (DOMelement.lastChild){
if (DOMelement.lastChild.lastChild) {
//also empty this child
this.empty(DOMelement.lastChild);
}
if ("attachedListeners" in DOMelement.lastChild) {
//remove attached event listeners
for (var i in DOMelement.lastChild.attachedListeners) {
MistUtil.event.removeListener(DOMelement.lastChild.attachedListeners[i]);
}
}
DOMelement.removeChild(DOMelement.lastChild);
}
},
event: {
send: function(type,message,target){
try {
var event = new Event(type,{
bubbles: true,
cancelable: true
});
event.message = message;
target.dispatchEvent(event);
}
catch (e) {
try {
var event = document.createEvent('Event');
event.initEvent(type,true,true);
event.message = message;
target.dispatchEvent(event);
}
catch (e) { return false; }
}
return true;
},
addListener: function(DOMelement,type,callback,storeOnElement) {
//add an event listener and store the handles, so they can be cleared
DOMelement.addEventListener(type,callback);
if (!storeOnElement) { storeOnElement = DOMelement; }
if (!("attachedListeners" in storeOnElement)) {
storeOnElement.attachedListeners = [];
}
var output = {
element: DOMelement,
type: type,
callback: callback
};
storeOnElement.attachedListeners.push(output);
return output;
},
removeListener: function(data) {
data.element.removeEventListener(data.type, data.callback);
}
},
scripts: {
list: {},
insert: function(src,onevent,MistVideo){
var scripts = this;
if (MistVideo) {
//register so we can remove it on unload
MistVideo.errorListeners.push({
src: src,
onevent: onevent
});
}
if (src in this.list) {
//already present
//register to error listening
this.list[src].subscribers.push(onevent.onerror);
//execute onload
if ("onload" in onevent) {
if (this.hasLoaded) {
onevent.onload();
}
else {
MistUtil.event.addListener(this.list[src].tag,"load",onevent.onload);
}
}
return;
}
var scripttag = document.createElement("script");
scripttag.hasLoaded = false;
scripttag.setAttribute("src",src);
scripttag.setAttribute("crossorigin","anonymous"); //must be set to get info about errors thrown
document.head.appendChild(scripttag);
scripttag.onerror = function(e){
onevent.onerror(e);
}
scripttag.onload = function(e){
this.hasLoaded = true;
if (!MistVideo.destroyed) { onevent.onload(e); }
}
scripttag.addEventListener("error",function(e){
onevent.onerror(e);
});
//error catching
var oldonerror = false;
if (window.onerror) {
oldonerror = window.onerror;
}
window.onerror = function(message,source,line,column,error){
if (oldonerror) {
oldonerror.apply(this,arguments);
}
if (source == src) {
onevent.onerror(error);
for (var i in scripts.list[src].subscribers) {
scripts.list[src].subscribers[i](error);
}
}
};
this.list[src] = {
subscribers: [onevent.onerror],
tag: scripttag
};
return scripttag;
}
},
tracks: {
parse: function(metaTracks){
var output = {};
for (var i in metaTracks) {
var track = MistUtil.object.extend({},metaTracks[i]);
if (track.type == "meta") {
track.type = track.codec;
track.codec = "meta";
}
if (!(track.type in output)) { output[track.type] = {}; }
output[track.type][track.trackid] = track;
//make up something logical for the track displayname
var name = {};
for (var j in track) {
switch (j) {
case "width":
name[j] = track.width+"×"+track.height;
break;
case "bps":
if (track.codec == "meta") { continue; }
if (track.bps > 0) {
var val;
if (track.bps > 1024*1024/8) {
val = Math.round(track.bps/1024/1024*8)+"mbps";
}
else {
val = Math.round(track.bps/1024*8)+"kbps";
}
name[j] = val;
}
break;
case "fpks":
if (track.fpks > 0) {
name[j] = track.fpks/1e3+"fps";
}
break;
case "channels":
if (track.channels > 0) {
name[j] = (track.channels == 1 ? "Mono" : (track.channels == 2 ? "Stereo" : "Surround ("+track.channels+"ch)"));
}
break;
case "rate":
name[j] = Math.round(track.rate)+"Khz";
break;
case "language":
if (track[j] != "Undetermined") { name[j] = track[j]; }
break;
case "codec":
if (track.codec == "meta") { continue; }
name[j] = track[j];
break;
}
}
track.describe = name;
}
//filter what to display based on what is different
for (var type in output) {
var equal = false;
for (var i in output[type]) {
if (!equal) {
//fill equal with all the keys and values of the first track of this type
equal = MistUtil.object.extend({},output[type][i].describe);
continue;
}
if (MistUtil.object.keys(output[type]).length > 1) {
//if there is more than one track of this type
for (var j in output[type][i].describe) {
if (equal[j] != output[type][i].describe[j]) {
//remove key from equal if not equal
delete equal[j];
}
}
}
}
//apply
for (var i in output[type]) {
var different = {};
var same = {};
for (var j in output[type][i].describe) {
if (!(j in equal)){
different[j] = output[type][i].describe[j];
}
else {
same[j] = output[type][i].describe[j];
}
}
output[type][i].different = different;
output[type][i].same = same;
var d = MistUtil.object.values(different);
output[type][i].displayName = (d.length ? d.join(", ") : MistUtil.object.values(output[type][i].describe).join(" "));
}
}
return output;
}
},
isTouchDevice: function(){
return (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0));
//return true;
},
getPos: function(element,cursorLocation){
var style = element.currentStyle || window.getComputedStyle(element, null);
var zoom = 1;
var node = element;
while (node) {
if ((node.style.zoom) && (node.style.zoom != "")) {
zoom *= parseFloat(node.style.zoom,10);
}
node = node.parentElement;
}
var pos0 = element.getBoundingClientRect().left - (parseInt(element.borderLeftWidth,10) || 0);
var width = element.getBoundingClientRect().width;;
var perc = Math.max(0,((cursorLocation.clientX/zoom) - pos0) / width);
perc = Math.min(perc,1);
return perc;
},
createGraph: function(data,options){
var ns = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(ns,"svg");
svg.setAttributeNS(null,"height","100%");
svg.setAttributeNS(null,"width","100%");
svg.setAttributeNS(null,"class","mist icon graph");
svg.setAttributeNS(null,"preserveAspectRatio","none");
var x_correction = data.x[0];
var lasty = data.y[0];
if (options.differentiate) {
for (var i = 1; i < data.y.length; i++) {
var diff = data.y[i] - lasty;
lasty = data.y[i];
data.y[i] = diff;
}
}
var path = [];
var area = {
x: {
min: data.x[0] - x_correction,
max: data.x[0] - x_correction
},
y: {
min: data.y[0]*-1,
max: data.y[0]*-1
}
};
function updateMinMax(x,y) {
if (arguments.length) {
area.x.min = Math.min(area.x.min,x);
area.x.max = Math.max(area.x.max,x);
area.y.min = Math.min(area.y.min,y*-1);
area.y.max = Math.max(area.y.max,y*-1);
}
else {
//reprocess the entire path
var d = path[0].split(",");
area = {
x: {
min: d[0],
max: d[0]
},
y: {
min: d[1],
max: d[1]
}
};
for (var i = 1; i < path.length; i++) {
var d = path[i].split(",");
updateMinMax(d[0],d[1]*-1);
}
}
}
path.push([data.x[0] - x_correction,data.y[0]*-1].join(","));
for (var i = 1; i < data.y.length; i++) {
updateMinMax(data.x[i] - x_correction,data.y[i]*-1);
path.push("L "+[data.x[i] - x_correction,data.y[i]*-1].join(","));
}
//define gradient
var defs = document.createElementNS(ns,"defs");
svg.appendChild(defs);
var gradient = document.createElementNS(ns,"linearGradient");
defs.appendChild(gradient);
gradient.setAttributeNS(null,"id",MistUtil.createUnique());
gradient.setAttributeNS(null,"gradientUnits","userSpaceOnUse");
gradient.innerHTML += '<stop offset="0" stop-color="green"/>';
gradient.innerHTML += '<stop offset="0.33" stop-color="yellow"/>';
gradient.innerHTML += '<stop offset="0.66" stop-color="orange"/>';
gradient.innerHTML += '<stop offset="1" stop-color="red"/>';
function updateViewBox() {
if ("x" in options) {
if ("min" in options.x) { area.x.min = options.x.min; }
if ("max" in options.x) { area.x.max = options.x.max; }
if ("count" in options.x) { area.x.min = area.x.max - options.x.count; }
}
if ("y" in options) {
if ("min" in options.y) { area.y.min = options.y.max*-1; }
if ("max" in options.y) { area.y.max = options.y.min*-1; }
}
svg.setAttributeNS(null,"viewBox",[area.x.min,area.y.min,area.x.max - area.x.min,area.y.max - area.y.min].join(" "));
gradient.setAttributeNS(null,"x1",0);
gradient.setAttributeNS(null,"x2",0);
if (options.reverseGradient) {
gradient.setAttributeNS(null,"y1",area.y.max);
gradient.setAttributeNS(null,"y2",area.y.min);
}
else {
gradient.setAttributeNS(null,"y1",area.y.min);
gradient.setAttributeNS(null,"y2",area.y.max);
}
}
updateViewBox();
var line = document.createElementNS(ns,"path");
svg.appendChild(line);
//line.setAttributeNS(null,"vector-effect","non-scaling-stroke");
line.setAttributeNS(null,"stroke-width","0.1");
line.setAttributeNS(null,"fill","none");
line.setAttributeNS(null,"stroke","url(#"+gradient.getAttribute("id")+")");
line.setAttributeNS(null,"d","M"+path.join(" L"));
line.addData = function(newData) {
if (options.differentiate) {
var diff = newData.y - lasty;
lasty = newData.y;
newData.y = diff;
}
path.push([newData.x - x_correction,newData.y*-1].join(","));
if (options.x && options.x.count) {
if (path.length > options.x.count) {
path.shift();
updateMinMax();
}
}
updateMinMax(newData.x - x_correction,newData.y*-1);
this.setAttributeNS(null,"d","M"+path.join(" L"));
updateViewBox();
};
svg.addData = function(newData){
line.addData(newData);
};
return svg;
},
getBrowser: function(){
var ua = window.navigator.userAgent;
if ((ua.indexOf("MSIE ") >= 0) || (ua.indexOf("Trident/") >= 0)) {
return "ie";
}
if (ua.indexOf("Edge/") >= 0) {
return "edge";
}
if ((ua.indexOf("Opera") >= 0) || (ua.indexOf('OPR') >= 0)) {
return "opera";
}
if (ua.indexOf("Chrome") >= 0) {
return "chrome";
}
if (ua.indexOf("Safari") >= 0) {
return "safari";
}
if (ua.indexOf("Firefox") >= 0) {
return "firefox";
}
return false; //unknown
}
};

View file

@ -1,162 +1,216 @@
mistplayers.dashjs = { mistplayers.dashjs = {
name: 'Dash.js Player', name: "Dash.js player",
version: '1.2', mimes: ["dash/video/mp4"/*,"html5/application/vnd.ms-ss"*/],
mimes: ['dash/video/mp4'], priority: MistUtil.object.keys(mistplayers).length + 1,
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) { isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true); return (MistUtil.array.indexOf(this.mimes,mimetype) == -1 ? false : true);
}, },
isBrowserSupported: function (mimetype,source,options) { isBrowserSupported: function (mimetype,source,MistVideo) {
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) { return false; }
return (('MediaSource' in window) && (location.protocol != 'file:')); //check for http/https mismatch
if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
MistVideo.log("HTTP/HTTPS mismatch for this source");
return false;
}
//don't use dashjs if this location is loaded over file://
if (location.protocol == "file:") {
MistVideo.log("This source ("+mimetype+") won't load if the page is run via file://");
return false;
}
return ("MediaSource" in window);
}, },
player: function(){this.onreadylist = [];} player: function(){this.onreadylist = [];},
scriptsrc: function(host) { return host+"/dashjs.js"; }
}; };
var p = mistplayers.dashjs.player; var p = mistplayers.dashjs.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) { p.prototype.build = function (MistVideo,callback) {
var cont = document.createElement('div');
cont.className = 'mistplayer';
var me = this; var me = this;
function onplayerload () { this.onDashLoad = function() {
var ele = me.getElement('video'); if (MistVideo.destroyed) { return; }
ele.className = '';
cont.appendChild(ele);
ele.width = options.width;
ele.height = options.height;
if (options.autoplay) { MistVideo.log("Building DashJS player..");
ele.setAttribute('autoplay','');
}
if (options.loop) {
ele.setAttribute('loop','');
}
if (options.poster) {
ele.setAttribute('poster',options.poster);
}
if (options.controls) {
if (options.controls == 'stock') {
ele.setAttribute('controls','');
}
else {
me.buildMistControls();
}
}
ele.addEventListener('error',function(e){ var ele = document.createElement("video");
var msg;
if ('message' in e) { if ("Proxy" in window) {
msg = e.message; var overrides = {
} get: {},
else { set: {}
msg = 'readyState: '; };
switch (me.element.readyState) {
case 0: MistVideo.player.api = new Proxy(ele,{
msg += 'HAVE_NOTHING'; get: function(target, key, receiver){
break; if (key in overrides.get) {
case 1: return overrides.get[key].apply(target, arguments);
msg += 'HAVE_METADATA'; }
break; var method = target[key];
case 2: if (typeof method === "function"){
msg += 'HAVE_CURRENT_DATA'; return function () {
break; return method.apply(target, arguments);
case 3: }
msg += 'HAVE_FUTURE_DATA'; }
break; return method;
case 4: },
msg += 'HAVE_ENOUGH_DATA'; set: function(target, key, value) {
break; if (key in overrides.set) {
return overrides.set[key].call(target,value);
}
return target[key] = value;
} }
msg += ' networkState: ';
switch (me.element.networkState) {
case 0:
msg += 'NETWORK_EMPTY';
break;
case 1:
msg += 'NETWORK_IDLE';
break;
case 2:
msg += 'NETWORK_LOADING';
break;
case 3:
msg += 'NETWORK_NO_SOURCE';
break;
}
}
//prevent onerror loops
if (e.target == me.element) {
e.message = msg;
}
else {
me.adderror(msg);
}
});
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting'];
for (var i in events) {
ele.addEventListener(events[i],function(e){
me.addlog('Player event fired: '+e.type);
}); });
if (MistVideo.info.type == "live") {
overrides.get.duration = function(){
//this should indicate the end of Mist's buffer
var buffer_end = 0;
if (this.buffered.length) {
buffer_end = this.buffered.end(this.buffered.length-1)
}
var time_since_buffer = (new Date().getTime() - MistVideo.player.api.lastProgress.getTime())*1e-3;
return buffer_end + time_since_buffer + -1*MistVideo.player.api.liveOffset + 45;
};
overrides.set.currentTime = function(value){
var offset = value - MistVideo.player.api.duration;
//MistVideo.player.api.liveOffset = offset;
MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
MistVideo.video.currentTime = value;
//.player.api.setSource(MistUtil.http.url.addParam(MistVideo.source.url,{startunix:offset}));
}
MistUtil.event.addListener(ele,"progress",function(){
MistVideo.player.api.lastProgress = new Date();
});
MistVideo.player.api.lastProgress = new Date();
MistVideo.player.api.liveOffset = 0;
}
}
else {
me.api = ele;
}
if (MistVideo.options.autoplay) {
ele.setAttribute("autoplay","");
}
if ((MistVideo.options.loop) && (MistVideo.info.type != "live")) {
ele.setAttribute("loop","");
}
if (MistVideo.options.poster) {
ele.setAttribute("poster",MistVideo.options.poster);
}
if (MistVideo.options.controls == "stock") {
ele.setAttribute("controls","");
} }
var player = dashjs.MediaPlayer().create(); var player = dashjs.MediaPlayer().create();
player.getDebug().setLogToBrowserConsole(false); //player.getDebug().setLogToBrowserConsole(false);
player.initialize(ele,options.src,true); player.initialize(ele,MistVideo.source.url,MistVideo.options.autoplay);
me.dash = player; me.dash = player;
me.src = options.src;
//add listeners for events that we can log
var skipEvents = ["METRIC_ADDED","METRIC_CHANGED","METRICS_CHANGED","FRAGMENT_LOADING_STARTED","FRAGMENT_LOADING_COMPLETED","LOG","PLAYBACK_TIME_UPDATED","PLAYBACK_PROGRESS"];
for (var i in dashjs.MediaPlayer.events) {
if (skipEvents.indexOf(i) < 0) {
me.dash.on(dashjs.MediaPlayer.events[i],function(e){
MistVideo.log("Player event fired: "+e.type);
});
}
}
me.addlog('Built html'); MistVideo.player.setSize = function(size){
callback(cont); this.api.style.width = size.width+"px";
this.api.style.height = size.height+"px";
};
MistVideo.player.api.setSource = function(url) {
MistVideo.player.dash.attachSource(url);
};
//trackswitching
MistVideo.player.api.setTrack = function(type,id){
var meta = MistUtil.tracks.parse(MistVideo.info.meta.tracks);
if ((!(type in meta)) || ((!(id in meta[type]) && (id != 0)))) {
MistVideo.log("Skipping trackselection of "+type+" track "+id+" because it does not exist");
return;
}
//figure out what the track number is
//whyyyy did it have to be reverse order
var n = me.dash.getBitrateInfoListFor("video").length - 1;
for (var i in MistVideo.info.meta.tracks) {
var t = MistVideo.info.meta.tracks[i];
if (t.type == type) {
if (t.trackid == id) { break; }
n--;
}
}
me.dash.setAutoSwitchQualityFor(type,false); //turn off ABR rules //TODO do we want this by default?
me.dash.setFastSwitchEnabled(true); //show the new track asap
me.dash.setQualityFor(type,n);
//dash does change the track, but is appended to the buffer, so it seems to take a while..
}
//react to automatic trackswitching
me.dash.on("qualityChangeRendered",function(e){
//the newQuality-th track of type mediaType is being selected
//figure out the track id
//whyyyy did it have to be reverse order
var n = me.dash.getBitrateInfoListFor("video").length - 1;
var id;
for (var i in MistVideo.info.meta.tracks) {
var t = MistVideo.info.meta.tracks[i];
if (t.type == e.mediaType) {
if (e.newQuality == n) {
id = t.trackid;
break;
}
n--;
}
}
//create an event to pass this to the skin
MistUtil.event.send("playerUpdate_trackChanged",{
type: e.mediaType,
trackid: id
},MistVideo.video);
});
//dashjs keeps on spamming the stalled icon >_>
MistUtil.event.addListener(ele,"progress",function(e){
if (MistVideo.container.getAttribute("data-loading") == "stalled") {
MistVideo.container.removeAttribute("data-loading");
}
});
me.api.unload = function(){
me.dash.reset();
};
MistVideo.log("Built html");
callback(ele);
} }
if ('dash' in window) { if ("dashjs" in window) {
onplayerload(); this.onDashLoad();
} }
else { else {
//load the dashjs player
var scripttag = document.createElement('script'); var scripttag = MistUtil.scripts.insert(MistVideo.urlappend(mistplayers.dashjs.scriptsrc(MistVideo.options.host)),{
scripttag.src = options.host+'/dashjs.js'; onerror: function(e){
me.addlog('Retrieving dashjs player code from '+scripttag.src); var msg = "Failed to load dashjs.js";
document.head.appendChild(scripttag); if (e.message) { msg += ": "+e.message; }
scripttag.onerror = function(){ MistVideo.showError(msg);
me.askNextCombo('Failed to load dashjs.js'); },
} onload: me.onDashLoad
scripttag.onload = function(){ },MistVideo);
onplayerload();
}
} }
} }
p.prototype.play = function(){ return this.element.play(); };
p.prototype.pause = function(){ return this.element.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.element.volume; }
return this.element.volume = level;
};
p.prototype.play = function(){ return this.element.play(); };
p.prototype.load = function(){ return this.element.load(); };
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
p.prototype.fullscreen = function(){
if(this.element.requestFullscreen) {
return this.element.requestFullscreen();
} else if(this.element.mozRequestFullScreen) {
return this.element.mozRequestFullScreen();
} else if(this.element.webkitRequestFullscreen) {
return this.element.webkitRequestFullscreen();
} else if(this.element.msRequestFullscreen) {
return this.element.msRequestFullscreen();
}
};
}
p.prototype.resize = function(size){
this.element.width = size.width;
this.element.height = size.height;
};
p.prototype.updateSrc = function(src){
if (src == '') {
this.dash.reset();
return;
}
this.dash.attachSource(src);
return true;
};

View file

@ -1,26 +1,37 @@
mistplayers.flash_strobe = { mistplayers.flash_strobe = {
name: 'Strobe Flash Media Playback', name: "Strobe Flash media playback",
version: '1.1', mimes: ["flash/10","flash/11","flash/7"],
mimes: ['flash/10','flash/11','flash/7'], priority: MistUtil.object.keys(mistplayers).length + 1,
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) { isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true); return (this.mimes.indexOf(mimetype) == -1 ? false : true);
}, },
isBrowserSupported: function (mimetype,source,options) { isBrowserSupported: function (mimetype,source,MistVideo) {
//check for http / https crossovers
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) { return false; } //check for http/https mismatch
if ((MistUtil.http.url.split(source.url).protocol.slice(0,4) == "http") && (location.protocol != MistUtil.http.url.split(source.url).protocol)) {
MistVideo.log("HTTP/HTTPS mismatch for this source");
return false;
}
var version = 0; var version = 0;
try { try {
// check in the mimeTypes // check in the mimeTypes
version = navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin.description.replace(/([^0-9\.])/g, '').split('.')[0]; var plugin = navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin;
if (plugin.version) { version = plugin.version.split(".")[0]; }
else { version = plugin.description.replace(/([^0-9\.])/g, "").split(".")[0]; }
} catch(e){} } catch(e){}
try { try {
// for our special friend IE // for our special friend IE
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable("$version").replace(/([^0-9\,])/g, '').split(',')[0]; version = new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/([^0-9\,])/g, "").split(",")[0];
} catch(e){} } catch(e){}
var mimesplit = mimetype.split('/'); if (!version) {
//flash is not enabled? Might need to ask permission first.
//TODO how? just let it build?
return false;
}
var mimesplit = mimetype.split("/");
return Number(version) >= Number(mimesplit[mimesplit.length-1]); return Number(version) >= Number(mimesplit[mimesplit.length-1]);
}, },
@ -28,39 +39,53 @@ mistplayers.flash_strobe = {
}; };
var p = mistplayers.flash_strobe.player; var p = mistplayers.flash_strobe.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) { p.prototype.build = function (MistVideo,callback) {
function createParam(name,value) {
var p = document.createElement('param');
p.setAttribute('name',name);
p.setAttribute('value',value);
return p;
}
var ele = document.createElement("object");
var ele = this.getElement('object'); var e = document.createElement("embed");
ele.setAttribute('width',options.width);
ele.setAttribute('height',options.height);
ele.appendChild(createParam('movie',options.host+options.source.player_url));
var flashvars = 'src='+encodeURIComponent(options.src)+'&controlBarMode='+(options.controls ? 'floating' : 'none')+'&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3'+(options.live ? '&streamType=live' : '')+(options.autoplay ? '&autoPlay=true' : '' );
ele.appendChild(createParam('flashvars',flashvars));
ele.appendChild(createParam('allowFullScreen','true'));
ele.appendChild(createParam('wmode','direct'));
if (options.autoplay) {
ele.appendChild(createParam('autoPlay','true'));
}
var e = document.createElement('embed');
ele.appendChild(e); ele.appendChild(e);
e.setAttribute('src',options.source.player_url);
e.setAttribute('type','application/x-shockwave-flash');
e.setAttribute('allowfullscreen','true');
e.setAttribute('width',options.width);
e.setAttribute('height',options.height);
e.setAttribute('flashvars',flashvars);
function build(source) {
var options = MistVideo.options;
function createParam(name,value) {
var p = document.createElement("param");
p.setAttribute("name",name);
p.setAttribute("value",value);
return p;
}
MistUtil.empty(ele);
ele.appendChild(createParam("movie",MistVideo.urlappend(options.host+MistVideo.source.player_url)));
var flashvars = "src="+encodeURIComponent(source)+"&controlBarMode="+(options.controls ? "floating" : "none")+"&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3"+(options.live ? "&streamType=live" : "")+(options.autoplay ? "&autoPlay=true" : "" );
ele.appendChild(createParam("flashvars",flashvars));
ele.appendChild(createParam("allowFullScreen","true"));
ele.appendChild(createParam("wmode","direct"));
if (options.autoplay) {
ele.appendChild(createParam("autoPlay","true"));
}
e.setAttribute("src",MistVideo.urlappend(MistVideo.source.player_url));
e.setAttribute("type","application/x-shockwave-flash");
e.setAttribute("allowfullscreen","true");
e.setAttribute("flashvars",flashvars);
}
build(MistVideo.source.url);
this.addlog('Built html'); this.api = {};
this.setSize = function(size){
ele.setAttribute("width",size.width);
ele.setAttribute("height",size.height);
e.setAttribute("width",size.width);
e.setAttribute("height",size.height);
};
this.setSize(MistVideo.calcSize());
this.onready(function(){
if (MistVideo.container) { MistVideo.container.removeAttribute("data-loading"); }
});
this.api.setSource = function(url){
build(url);
}
MistVideo.log("Built html");
callback(ele); callback(ele);
} }

View file

@ -1,29 +1,87 @@
mistplayers.html5 = { mistplayers.html5 = {
name: 'HTML5 video player', name: "HTML5 video player",
version: '1.1', mimes: ["html5/application/vnd.apple.mpegurl","html5/video/mp4","html5/video/ogg","html5/video/webm","html5/audio/mp3","html5/audio/webm","html5/audio/ogg","html5/audio/wav"],
mimes: ['html5/application/vnd.apple.mpegurl','html5/video/mp4','html5/video/ogg','html5/video/webm','html5/audio/mp3','html5/audio/webm','html5/audio/ogg','html5/audio/wav'], priority: MistUtil.object.keys(mistplayers).length + 1,
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) { isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true); return (MistUtil.array.indexOf(this.mimes,mimetype) == -1 ? false : true);
}, },
isBrowserSupported: function (mimetype,source,options,streaminfo) { isBrowserSupported: function (mimetype,source,MistVideo) {
if ((['iPad','iPhone','iPod','MacIntel'].indexOf(navigator.platform) != -1) && (mimetype == 'html5/video/mp4')) { return false; }
var support = false; //check for http/https mismatch
var shortmime = mimetype.split('/'); if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
shortmime.shift(); if ((location.protocol == "file:") && (MistUtil.http.url.split(source.url).protocol == "http:")) {
MistVideo.log("This page was loaded over file://, the player might not behave as intended.");
if ((shortmime[0] == 'audio') && (streaminfo.height) && (!options.forceType) && (!options.forceSource)) { }
//claim you don't support audio-only playback if there is video data, unless this mime is being forced else {
return false; MistVideo.log("HTTP/HTTPS mismatch for this source");
return false;
}
} }
var support = false;
var shortmime = mimetype.split("/");
shortmime.shift();
try { try {
var v = document.createElement((shortmime[0] == 'audio' ? 'audio' : 'video')); shortmime = shortmime.join("/");
shortmime = shortmime.join('/')
if ((v) && (v.canPlayType(shortmime) != "")) { function test(mime) {
support = v.canPlayType(shortmime); var v = document.createElement("video");
if ((v) && (v.canPlayType(mime) != "")) {
support = v.canPlayType(mime);
}
return support;
} }
if (shortmime == "video/mp4") {
function translateCodec(track) {
function bin2hex(index) {
return ("0"+track.init.charCodeAt(index).toString(16)).slice(-2);
}
switch (track.codec) {
case "AAC":
return "mp4a.40.2";
case "MP3":
return "mp3";
//return "mp4a.40.34";
case "AC3":
return "ec-3";
case "H264":
return "avc1."+bin2hex(1)+bin2hex(2)+bin2hex(3);
case "HEVC":
return "hev1."+bin2hex(1)+bin2hex(6)+bin2hex(7)+bin2hex(8)+bin2hex(9)+bin2hex(10)+bin2hex(11)+bin2hex(12);
default:
return track.codec.toLowerCase();
}
}
var codecs = {};
for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type != "meta") {
codecs[translateCodec(MistVideo.info.meta.tracks[i])] = 1;
}
}
codecs = MistUtil.object.keys(codecs);
if (codecs.length) {
if (codecs.length > source.simul_tracks) {
//not all of the tracks have to work
var working = 0;
for (var i in codecs) {
var s = test(shortmime+";codecs=\""+codecs[i]+"\"");
if (s) {
working++;
}
}
return (working >= source.simul_tracks);
}
shortmime += ";codecs=\""+codecs.join(",")+"\"";
}
}
support = test(shortmime);
} catch(e){} } catch(e){}
return support; return support;
}, },
@ -34,206 +92,159 @@ mistplayers.html5 = {
}; };
var p = mistplayers.html5.player; var p = mistplayers.html5.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) { p.prototype.build = function (MistVideo,callback) {
var cont = document.createElement('div'); var shortmime = MistVideo.source.type.split("/");
cont.className = 'mistplayer';
var me = this; //to allow nested functions to access the player class itself
var shortmime = options.source.type.split('/');
shortmime.shift(); shortmime.shift();
var video = document.createElement("video");
var ele = this.getElement((shortmime[0] == 'audio' ? 'audio' : 'video')); //TODO verify: not required if player is loaded from same domain as it should always be when not in dev mode?
ele.className = ''; video.setAttribute("crossorigin","anonymous");//required for subs, breaks ogg?
cont.appendChild(ele);
if (options.source.type != "html5/video/ogg") { var source = document.createElement("source");
ele.crossOrigin = 'anonymous'; //required for subtitles, but if ogg, the video won't load source.setAttribute("src",MistVideo.source.url);
} video.source = source;
video.appendChild(source);
source.type = shortmime.join("/");
if (shortmime[0] == 'audio') { //apply options
this.setTracks = function() { return false; } var attrs = ["autoplay","loop","poster"];
this.fullscreen = false; for (var i in attrs) {
cont.className += ' audio'; var attr = attrs[i];
} if (MistVideo.options[attr]) {
video.setAttribute(attr,(MistVideo.options[attr] === true ? "" : MistVideo.options[attr]));
this.addlog('Building HTML5 player..');
var source = document.createElement('source');
source.setAttribute('src',options.src);
this.source = source;
ele.appendChild(source);
source.type = shortmime.join('/');
this.addlog('Adding '+source.type+' source @ '+options.src);
if ((this.tracks.subtitle.length) && (this.subtitle)) {
for (var i in this.tracks.subtitle) {
var t = document.createElement('track');
ele.appendChild(t);
t.kind = 'subtitles';
t.label = this.tracks.subtitle[i].desc;
t.srclang = this.tracks.subtitle[i].lang;
t.src = this.subtitle+'?track='+this.tracks.subtitle[i].trackid;
} }
} }
if (MistVideo.options.controls == "stock") {
ele.width = options.width; video.setAttribute("controls","");
ele.height = options.height;
ele.style.width = options.width+'px';
ele.style.height = options.height+'px';
if (options.autoplay) {
ele.setAttribute('autoplay','');
} }
if (options.loop) { if (MistVideo.info.type == "live") {
ele.setAttribute('loop',''); video.loop = false;
}
if (options.poster) {
ele.setAttribute('poster',options.poster);
}
if (options.controls) {
if ((options.controls == 'stock') || (!this.buildMistControls())) {
//MistControls have failed to build in the if condition
ele.setAttribute('controls','');
}
} }
cont.onclick = function(){ if (("Proxy" in window) && ("Reflect" in window)) {
if (ele.paused) { ele.play(); } var overrides = {
else { ele.pause(); } get: {},
}; set: {}
};
this.addlog('Built html');
//forward events
ele.addEventListener('error',function(e){
if (!e.isTrusted) { return; } //don't trigger on errors we have thrown ourselves
if (options.live) { MistVideo.player.api = new Proxy(video,{
if ((ele.error) && (ele.error.code == 3)) { get: function(target, key, receiver){
e.stopPropagation(); //dont let this error continue to prevent the core from trying to handle the error if (key in overrides.get) {
me.load(); return overrides.get[key].apply(target, arguments);
me.cancelAskNextCombo(); }
e.message = 'Handled decoding error'; var method = target[key];
me.addlog('Decoding error: reloading..'); if (typeof method === "function"){
me.report({ return function () {
type: 'playback', return method.apply(target, arguments);
warn: 'A decoding error was encountered, but handled' }
}); }
return; return method;
},
set: function(target, key, value) {
if (key in overrides.set) {
return overrides.set[key].call(target,value);
}
return target[key] = value;
} }
} });
var msg; if (MistVideo.source.type == "html5/audio/mp3") {
if ('message' in e) { overrides.set.currentTime = function(){
msg = e.message; MistVideo.log("Seek attempted, but MistServer does not currently support seeking in MP3.");
return false;
}
} }
else if ((e.target.tagName == 'SOURCE') && (e.target.getAttribute('src') == '')) { if (MistVideo.info.type == "live") {
e.stopPropagation();
//this error is triggered because the unload function was fired overrides.get.duration = function(){
return; //this should indicate the end of Mist's buffer
var buffer_end = 0;
if (this.buffered.length) {
buffer_end = this.buffered.end(this.buffered.length-1)
}
var time_since_buffer = (new Date().getTime() - MistVideo.player.api.lastProgress.getTime())*1e-3;
return buffer_end + time_since_buffer - MistVideo.player.api.liveOffset;
};
overrides.set.currentTime = function(value){
var offset = value - MistVideo.player.api.duration;
MistVideo.player.api.liveOffset = offset;
MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
MistVideo.player.api.setSource(MistUtil.http.url.addParam(MistVideo.source.url,{startunix:offset}));
}
MistUtil.event.addListener(video,"progress",function(){
MistVideo.player.api.lastProgress = new Date();
});
MistVideo.player.api.lastProgress = new Date();
MistVideo.player.api.liveOffset = 0;
MistUtil.event.addListener(video,"pause",function(){
MistVideo.player.api.pausedAt = new Date();
});
overrides.get.play = function(){
return function(){
if ((MistVideo.player.api.paused) && (MistVideo.player.api.pausedAt) && ((new Date()) - MistVideo.player.api.pausedAt > 5e3)) {
video.load();
MistVideo.log("Reloading source..");
}
return video.play.apply(video, arguments);
}
};
if (MistVideo.source.type == "html5/video/mp4") {
overrides.get.currentTime = function(){
return this.currentTime - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3;
}
}
} }
else { else {
msg = 'readyState: '; if (!isFinite(video.duration)) {
switch (me.element.readyState) { var duration = 0;
case 0: for (var i in MistVideo.info.meta.tracks) {
msg += 'HAVE_NOTHING'; duration = Math.max(duration,MistVideo.info.meta.tracks[i].lastms);
break; }
case 1: overrides.get.duration = function(){
msg += 'HAVE_METADATA'; if (isFinite(this.duration)) { return this.duration; }
break; return duration * 1e-3;
case 2: }
msg += 'HAVE_CURRENT_DATA';
break;
case 3:
msg += 'HAVE_FUTURE_DATA';
break;
case 4:
msg += 'HAVE_ENOUGH_DATA';
break;
}
msg += ' networkState: ';
switch (me.element.networkState) {
case 0:
msg += 'NETWORK_EMPTY';
break;
case 1:
msg += 'NETWORK_IDLE';
break;
case 2:
msg += 'NETWORK_LOADING';
break;
case 3:
msg += 'NETWORK_NO_SOURCE';
break;
} }
} }
me.adderror(msg);
});
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress'];
for (var i in events) {
ele.addEventListener(events[i],function(e){
me.addlog('Player event fired: '+e.type);
});
}
callback(cont);
}
p.prototype.play = function(){ return this.element.play(); };
p.prototype.pause = function(){ return this.element.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.element.volume; }
return this.element.volume = level;
};
p.prototype.loop = function(bool){
if (typeof bool == 'undefined') {
return this.element.loop;
}
return this.element.loop = bool;
};
p.prototype.load = function(){
var load;
if (this.element.paused) {
load = this.element.load();
} }
else { else {
//sometimes there is a play / pause interrupt: try again MistVideo.player.api = video;
//TODO figure out if this happens on paused or on playing
this.load();
return;
} }
MistVideo.player.api.setSource = function(url) {
//this helps to prevent the player from just showing a black screen after a reload if (url != this.source.src) {
if (this.element.paused) { this.source.src = url;
var me = this; this.load();
var unpause = function(){
if (me.element.paused) {
me.element.play();
}
me.element.removeEventListener('progress',unpause);
}
this.element.addEventListener('progress',unpause);
}
return load;
};
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
p.prototype.fullscreen = function(){
if(this.element.requestFullscreen) {
return this.element.requestFullscreen();
} else if(this.element.mozRequestFullScreen) {
return this.element.mozRequestFullScreen();
} else if(this.element.webkitRequestFullscreen) {
return this.element.webkitRequestFullscreen();
} else if(this.element.msRequestFullscreen) {
return this.element.msRequestFullscreen();
} }
}; };
MistVideo.player.api.setSubtitle = function(trackmeta) {
//remove previous subtitles
var tracks = video.getElementsByTagName("track");
for (var i = tracks.length - 1; i >= 0; i--) {
video.removeChild(tracks[i]);
}
if (trackmeta) { //if the chosen track exists
//add the new one
var track = document.createElement("track");
video.appendChild(track);
track.kind = "subtitles";
track.label = trackmeta.label;
track.srclang = trackmeta.lang;
track.src = trackmeta.src;
track.setAttribute("default","");
}
};
MistVideo.player.setSize = function(size){
this.api.style.width = size.width+"px";
this.api.style.height = size.height+"px";
};
callback(video);
} }
p.prototype.updateSrc = function(src){
this.source.setAttribute('src',src);
return true;
};
p.prototype.resize = function(size){
this.element.width = size.width;
this.element.height = size.height;
};

View file

@ -1,23 +0,0 @@
mistplayers.img = {
name: 'HTML img tag',
version: '1.1',
mimes: ['html5/image/jpeg'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype,source,options,streaminfo) {
//only use this if we are sure we just want an image
if ((options.forceType) || (options.forceSource) || (options.forcePlayer)) { return true; }
return false;
},
player: function(){this.onreadylist = [];}
};
var p = mistplayers.img.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
var ele = this.getElement('img');
ele.src = options.src;
ele.style.display = 'block';
callback(ele);
}

View file

@ -1,40 +0,0 @@
mistplayers.jwplayer = {
name: 'JWPlayer',
version: '0.2',
mimes: ['html5/video/mp4','html5/video/webm','dash/video/mp4','flash/10','flash/7','html5/application/vnd.apple.mpegurl','html5/audio/mp3','html5/audio/aac'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
//TODO like, actually check the browser or something?
if (typeof jwplayer == 'function') {
return true;
}
return false;
},
player: function(){}
};
var p = mistplayers.jwplayer.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
var ele = this.getElement('div');
this.jw = jwplayer(ele).setup({
file: options.src,
width: options.width,
height: options.height,
autostart: options.autoplay,
image: options.poster,
controls: options.controls
});
this.addlog('Built html');
callback(ele);
}
p.prototype.play = function(){ return this.jw.play(); };
p.prototype.pause = function(){ return this.jw.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.jw.getVolume/100; }
return this.jw.setVolume(level*100);
};

View file

@ -1,101 +0,0 @@
mistplayers.polytrope = {
name: 'Polytrope Flash Player',
version: '0.2',
mimes: ['flash/11','flash/10','flash/7'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
return false;
var version = 0;
try {
// check in the mimeTypes
version = navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin.description.replace(/([^0-9\.])/g, '').split('.')[0];
} catch(e){}
try {
// for our special friend IE
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable("$version").replace(/([^0-9\,])/g, '').split(',')[0];
} catch(e){}
var mimesplit = mimetype.split('/');
return Number(version) >= Number(mimesplit[mimesplit.length-1]);
},
player: function(){}
};
var p = mistplayers.polytrope.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
function createParam(name,value) {
var p = document.createElement('param');
p.setAttribute('name',name);
p.setAttribute('value',value);
return p;
}
//TODO its not working.
/*
this.swf = this.video_instance_el.flash({
swf: "/shared/swf/videoplayer.swf?" + (new Date).getTime(),
width: "100%",
height: parseInt(this.options.element.height()),
wmode: "opaque",
menu: "false",
allowFullScreen: "true",
allowFullScreenInteractive: "true",
allowScriptAccess: "always",
id: "cucumbertv-swf-" + this.guid,
expressInstall: "/shared/swf/expressInstall.swf",
flashvars: {
rtmp_url: "rtmp://" + this.options.stream_host + "/play/",
stream_name: this.options.stream_name,
poster: this.options.poster,
autoplay: this.options.autoplay,
color_1: "0x1d1d1d",
color_2: "0xffffff",
buffer_time: .1,
is_streaming_url: "/api/user/is_streaming",
username: this.options.username,
mode: "v" == this.options.type ? "archive" : "live",
guid: this.guid
}
})
<div>
<object data="/shared/swf/videoplayer.swf?1468312898591" type="application/x-shockwave-flash" id="cucumbertv-swf-4dc64c18-59af-91a2-d0c5-ab8df4f45c65" width="100%" height="660">
<param name="wmode" value="opaque">
<param name="menu" value="false">
<param name="allowFullScreen" value="true">
<param name="allowFullScreenInteractive" value="true">
<param name="allowScriptAccess" value="always">
<param name="expressInstall" value="/shared/swf/expressInstall.swf">
<param name="flashvars" value="rtmp_url=rtmp://www.stickystage.com/play/&amp;stream_name=stickystage_archive+SrA-2016.07.08.23.54.08&amp;poster=/stickystage/users/SrA/archive/SrA-2016.07.08.23.54.08.jpg&amp;autoplay=true&amp;color_1=0x1d1d1d&amp;color_2=0xffffff&amp;buffer_time=0.1&amp;is_streaming_url=/api/user/is_streaming&amp;username=SrA&amp;mode=archive&amp;guid=4dc64c18-59af-91a2-d0c5-ab8df4f45c65">
<param name="movie" value="/shared/swf/videoplayer.swf?1468312898591">
</object>
</div>
*/
var ele = this.element('object');
ele.data = 'players/polytrope.swf';
ele.type = 'application/x-shockwave-flash';
ele.width = options.width;
ele.height = options.height;
/*
ele.appendChild(createParam('allowFullScreen','true'));
ele.appendChild(createParam('allowScriptAccess','always'));
var flashvars = 'rtmp_url=rtmp://www.stickystage.com/play/&amp;stream_name=stickystage_archive+SrA-2016.07.08.23.54.08&amp;poster=/stickystage/users/SrA/archive/SrA-2016.07.08.23.54.08.jpg&amp;autoplay=true&amp;color_1=0x1d1d1d&amp;color_2=0xffffff&amp;buffer_time=0.1&amp;is_streaming_url=/api/user/is_streaming&amp;username=SrA&amp;mode=archive&amp;guid=4dc64c18-59af-91a2-d0c5-ab8df4f45c65';
ele.appendChild(createParam('flashvars',flashvars));
ele.appendChild(createParam('movie','players/polytrope.swf'));
*/
ele.innerHTML = '<param name="wmode" value="opaque"> <param name="menu" value="false"> <param name="allowFullScreen" value="true"> <param name="allowFullScreenInteractive" value="true"> <param name="allowScriptAccess" value="always"> <param name="expressInstall" value="/shared/swf/expressInstall.swf"> <param name="flashvars" value="rtmp_url=rtmp://www.stickystage.com/play/&amp;stream_name=stickystage_archive+SrA-2016.07.08.23.54.08&amp;poster=http://stickystage.com/stickystage/users/SrA/archive/SrA-2016.07.08.23.54.08.jpg&amp;autoplay=true&amp;color_1=0x1d1d1d&amp;color_2=0xffffff&amp;buffer_time=0.1&amp;is_streaming_url=/api/user/is_streaming&amp;username=SrA&amp;mode=archive&amp;guid=4dc64c18-59af-91a2-d0c5-ab8df4f45c65"> <param name="movie" value="players/polytrope.swf">';
this.addlog('Built html');
callback(ele);
}

View file

@ -1,56 +0,0 @@
mistplayers.silverlight = {
name: 'Silverlight',
version: '1.1',
mimes: ['silverlight'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
var plugin;
try {
// check in the mimeTypes
plugin = navigator.plugins["Silverlight Plug-In"];
return !!plugin;
} catch(e){}
try {
// for our special friend IE
plugin = new ActiveXObject('AgControl.AgControl');
return true;
} catch(e){}
return false;
},
player: function(){this.onreadylist = [];}
};
var p = mistplayers.silverlight.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
function createParam(name,value) {
var p = document.createElement('param');
p.setAttribute('name',name);
p.setAttribute('value',value);
return p;
}
var ele = this.getElement('object');
ele.setAttribute('data','data:application/x-silverlight,');
ele.setAttribute('type','application/x-silverlight');
ele.setAttribute('width',options.width);
ele.setAttribute('height',options.height);
ele.appendChild(createParam('source',encodeURI(options.src)+'/player.xap'));
ele.appendChild(createParam('initparams','autoload=false,'+(options.autoplay ? 'autoplay=true' : 'autoplay=false')+',displaytimecode=false,enablecaptions=true,joinLive=true,muted=false'));
var a = document.createElement('a');
ele.appendChild(a);
a.setAttribute('href','http://go.microsoft.com/fwlink/?LinkID=124807');
a.setAttribute('style','text-decoration: none;');
var img = document.createElement('img');
a.appendChild(img);
img.setAttribute('src','http://go.microsoft.com/fwlink/?LinkId=108181');
img.setAttribute('alt','Get Microsoft Silverlight');
img.setAttribute('style','border-style: none;')
this.addlog('Built html');
callback(ele);
}

View file

@ -1,24 +0,0 @@
mistplayers.myplayer = {
name: 'My video player',
version: '0.1',
mimes: ['my/mime/types'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
//TODO your code here
return false;
},
player: function(){}
};
var p = mistplayers.myplayer.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
var ele = this.element('object');
//TODO your code here
this.addlog('Built html');
callback(ele);
}

View file

@ -1,53 +0,0 @@
mistplayers.theoplayer = {
name: 'TheoPlayer',
version: '0.2',
mimes: ['html5/application/vnd.apple.mpegurl','dash/video/mp4'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
//TODO like, actually check the browser or something?
if (typeof theoplayer == 'function') {
return true;
}
return false;
},
player: function(){}
};
var p = mistplayers.theoplayer.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
var ele = this.getElement('video');
ele.src = options.src;
ele.width = options.width;
ele.height = options.height;
if (options.controls) {
ele.setAttribute('controls','');
}
if (options.autoplay) {
ele.setAttribute('autoplay','');
}
if (options.loop) {
ele.setAttribute('loop','');
}
if (options.poster) {
ele.setAttribute('poster',options.poster);
}
this.theoplayer = theoplayer(ele);
this.addlog('Built html');
callback(ele);
}
p.prototype.play = function(){ return this.theoplayer.play(); };
p.prototype.pause = function(){ return this.theoplayer.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.theoplayer.volume; }
return this.theoplayer.volume = level;
};
p.prototype.fullscreen = function(){
return this.theoplayer.requestFullscreen();
};

View file

@ -1,221 +1,208 @@
mistplayers.videojs = { mistplayers.videojs = {
name: 'VideoJS player', name: "VideoJS player",
version: '1.1', mimes: ["html5/application/vnd.apple.mpegurl"],
mimes: ['html5/video/mp4','html5/application/vnd.apple.mpegurl','html5/video/ogg','html5/video/webm'], priority: MistUtil.object.keys(mistplayers).length + 1,
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) { isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true); return (this.mimes.indexOf(mimetype) == -1 ? false : true);
}, },
isBrowserSupported: function (mimetype,source,options,streaminfo,logfunc) { isBrowserSupported: function (mimetype,source,MistVideo) {
//dont use https if the player is loaded over http //check for http/https mismatch
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) { if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
if (logfunc) { logfunc('HTTP/HTTPS mismatch for this source'); } MistVideo.log("HTTP/HTTPS mismatch for this source");
return false; return false;
} }
//dont use videojs if this location is loaded over file:// //don't use videojs if this location is loaded over file://
if ((location.protocol == 'file:') && (mimetype == 'html5/application/vnd.apple.mpegurl')) { if ((location.protocol == "file:") && (mimetype == "html5/application/vnd.apple")) {
if (logfunc) { logfunc('This source ('+mimetype+') won\'t work if the page is run via file://'); } MistVideo.log("This source ("+mimetype+") won't load if the page is run via file://");
return false; return false;
} }
//dont use HLS if there is an MP3 audio track, unless we're on apple or edge return ("MediaSource" in window);
if ((mimetype == 'html5/application/vnd.apple.mpegurl') && (['iPad','iPhone','iPod','MacIntel'].indexOf(navigator.platform) == -1) && (navigator.userAgent.indexOf('Edge') == -1)) {
var audio = false;
var nonmp3 = false;
for (var i in streaminfo.meta.tracks) {
var t = streaminfo.meta.tracks[i];
if (t.type == 'audio') {
audio = true;
if (t.codec != 'MP3') {
nonmp3 = true;
}
}
}
if ((audio) && (!nonmp3)) {
if (logfunc) { logfunc('This source has audio, but only MP3, and this browser can\'t play MP3 via HLS'); }
return false;
}
}
return ('MediaSource' in window);
}, },
player: function(){this.onreadylist = [];} player: function(){},
scriptsrc: function(host) { return host+"/videojs.js"; }
}; };
var p = mistplayers.videojs.player; var p = mistplayers.videojs.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) { p.prototype.build = function (MistVideo,callback) {
var me = this; //to allow nested functions to access the player class itself var me = this; //to allow nested functions to access the player class itself
function onplayerload () { function onVideoJSLoad () {
me.addlog('Building VideoJS player..'); if (MistVideo.destroyed) { return;}
var cont = document.createElement('div'); MistVideo.log("Building VideoJS player..");
cont.className = 'mistplayer';
var ele = me.getElement('video'); var ele = document.createElement("video");
cont.appendChild(ele); if (MistVideo.source.type != "html5/video/ogg") {
ele.className = ''; ele.crossOrigin = "anonymous"; //required for subtitles, but if ogg, the video won"t load
if (options.source.type != "html5/video/ogg") {
ele.crossOrigin = 'anonymous'; //required for subtitles, but if ogg, the video won't load
} }
var shortmime = options.source.type.split('/'); var shortmime = MistVideo.source.type.split("/");
shortmime.shift(); shortmime.shift();
var source = document.createElement('source'); var source = document.createElement("source");
source.setAttribute('src',options.src); source.setAttribute("src",MistVideo.source.url);
me.source = source; me.source = source;
ele.appendChild(source); ele.appendChild(source);
source.type = shortmime.join('/'); source.type = shortmime.join("/");
me.addlog('Adding '+source.type+' source @ '+options.src); MistVideo.log("Adding "+source.type+" source @ "+MistVideo.source.url);
if (source.type == 'application/vnd.apple.mpegurl') { source.type = 'application/x-mpegURL'; } if (source.type == "application/vnd.apple.mpegurl") { source.type = "application/x-mpegURL"; }
ele.className += ' video-js'; MistUtil.class.add(ele,"video-js");
ele.width = options.width;
ele.height = options.height;
ele.style.width = options.width+'px';
ele.style.height = options.height+'px';
var vjsopts = { var vjsopts = {};
preload: 'auto'
};
if (options.autoplay) { vjsopts.autoplay = true; } if (MistVideo.options.autoplay) { vjsopts.autoplay = true; }
if (options.loop) { if ((MistVideo.options.loop) && (MistVideo.info.type != "live")) {
vjsopts.loop = true; vjsopts.loop = true;
ele.loop = true; ele.loop = true;
} }
if (options.poster) { vjsopts.poster = options.poster; } if (MistVideo.options.poster) { vjsopts.poster = MistVideo.options.poster; }
if (options.controls) { if (MistVideo.options.controls == "stock") {
if ((options.controls == 'stock') || (!me.buildMistControls())) { ele.setAttribute("controls","");
//MistControls have failed to build in the if condition if (!document.getElementById("videojs-css")) {
ele.setAttribute('controls',true); var style = document.createElement("link");
style.rel = "stylesheet";
style.href = MistVideo.options.host+"/skins/videojs.css";
style.id = "videojs-css";
document.head.appendChild(style);
} }
} }
me.onready(function(){ me.onready(function(){
me.videojs = videojs(ele,vjsopts,function(){ me.videojs = videojs(ele,vjsopts,function(){
me.addlog('Videojs initialized'); MistVideo.log("Videojs initialized");
}); });
me.api.unload = function(){
videojs(ele).dispose();
};
}); });
me.addlog('Built html'); MistVideo.log("Built html");
//forward events if (("Proxy" in window) && ("Reflect" in window)) {
ele.addEventListener('error',function(e){ var overrides = {
if (!e.isTrusted) { return; } //don't trigger on errors we have thrown ourselves get: {},
set: {}
};
var msg; MistVideo.player.api = new Proxy(ele,{
if ('message' in e) { get: function(target, key, receiver){
msg = e.message; if (key in overrides.get) {
} return overrides.get[key].apply(target, arguments);
else { }
msg = 'readyState: '; var method = target[key];
switch (me.element.readyState) { if (typeof method === "function"){
case 0: return function () {
msg += 'HAVE_NOTHING'; return method.apply(target, arguments);
break; }
case 1: }
msg += 'HAVE_METADATA'; return method;
break; },
case 2: set: function(target, key, value) {
msg += 'HAVE_CURRENT_DATA'; if (key in overrides.set) {
break; return overrides.set[key].call(target,value);
case 3: }
msg += 'HAVE_FUTURE_DATA'; return target[key] = value;
break;
case 4:
msg += 'HAVE_ENOUGH_DATA';
break;
} }
msg += ' networkState: ';
switch (me.element.networkState) {
case 0:
msg += 'NETWORK_EMPTY';
break;
case 1:
msg += 'NETWORK_IDLE';
break;
case 2:
msg += 'NETWORK_LOADING';
break;
case 3:
msg += 'NETWORK_NO_SOURCE';
break;
}
}
me.adderror(msg);
});
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress'];
for (var i in events) {
ele.addEventListener(events[i],function(e){
me.addlog('Player event fired: '+e.type);
}); });
if (MistVideo.info.type == "live") {
function getLastBuffer(video) {
var buffer_end = 0;
if (video.buffered.length) {
buffer_end = video.buffered.end(video.buffered.length-1)
}
return buffer_end;
}
var HLSlatency = 90; //best guess..
overrides.get.duration = function(){
return (MistVideo.info.lastms + (new Date()).getTime() - MistVideo.info.updated.getTime())*1e-3;
};
MistVideo.player.api.lastProgress = new Date();
MistVideo.player.api.liveOffset = 0;
MistUtil.event.addListener(ele,"progress",function(){
MistVideo.player.api.lastProgress = new Date();
});
overrides.set.currentTime = function(value){
var diff = MistVideo.player.api.currentTime - value;
var offset = value - MistVideo.player.api.duration;
//MistVideo.player.api.liveOffset = offset;
MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
MistVideo.video.currentTime -= diff;
}
overrides.get.currentTime = function(){
return this.currentTime + MistVideo.info.lastms*1e-3 - MistVideo.player.api.liveOffset - HLSlatency;
}
}
}
else {
me.api = ele;
} }
callback(cont); MistVideo.player.setSize = function(size){
if ("videojs" in MistVideo.player) {
MistVideo.player.videojs.dimensions(size.width,size.height);
//for some reason, the videojs' container won't be resized with the method above.
//so let's cheat and do it ourselves
ele.parentNode.style.width = size.width+"px";
ele.parentNode.style.height = size.height+"px";
}
this.api.style.width = size.width+"px";
this.api.style.height = size.height+"px";
};
MistVideo.player.api.setSource = function(url) {
if (!MistVideo.player.videojs) { return; }
if (MistVideo.player.videojs.src() != url) {
MistVideo.player.videojs.src({
type: MistVideo.player.videojs.currentSource().type,
src: url
});
}
};
MistVideo.player.api.setSubtitle = function(trackmeta) {
//remove previous subtitles
var tracks = ele.getElementsByTagName("track");
for (var i = tracks.length - 1; i >= 0; i--) {
ele.removeChild(tracks[i]);
}
if (trackmeta) { //if the chosen track exists
//add the new one
var track = document.createElement("track");
ele.appendChild(track);
track.kind = "subtitles";
track.label = trackmeta.label;
track.srclang = trackmeta.lang;
track.src = trackmeta.src;
track.setAttribute("default","");
}
};
callback(ele);
} }
if ('videojs' in window) { if ("videojs" in window) {
onplayerload(); onVideoJSLoad();
} }
else { else {
//load the videojs player //load the videojs player
var scripttag = document.createElement('script');
scripttag.src = options.host+'/videojs.js'; var scripttag = MistUtil.scripts.insert(MistVideo.urlappend(mistplayers.videojs.scriptsrc(MistVideo.options.host)),{
me.addlog('Retrieving videojs player code from '+scripttag.src); onerror: function(e){
document.head.appendChild(scripttag); var msg = "Failed to load videojs.js";
scripttag.onerror = function(){ if (e.message) { msg += ": "+e.message; }
me.askNextCombo('Failed to load videojs.js'); MistVideo.showError(msg);
} },
scripttag.onload = function(){ onload: onVideoJSLoad
onplayerload(); },MistVideo);
}
} }
} }
p.prototype.play = function(){ return this.element.play(); };
p.prototype.pause = function(){ return this.element.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.element.volume; }
return this.element.volume = level;
};
p.prototype.loop = function(bool){
if (typeof bool == 'undefined') {
return this.element.loop;
}
return this.element.loop = bool;
};
p.prototype.load = function(){ return this.element.load(); };
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
p.prototype.fullscreen = function(){
if(this.element.requestFullscreen) {
return this.element.requestFullscreen();
} else if(this.element.mozRequestFullScreen) {
return this.element.mozRequestFullScreen();
} else if(this.element.webkitRequestFullscreen) {
return this.element.webkitRequestFullscreen();
} else if(this.element.msRequestFullscreen) {
return this.element.msRequestFullscreen();
}
};
}
p.prototype.updateSrc = function(src){
if ("videojs" in this) {
if (src == '') {
this.videojs.dispose();
return;
}
this.videojs.src({
src: src,
type: this.source.type
});
return true;
}
return false;
};

429
embed/wrappers/webrtc.js Normal file
View file

@ -0,0 +1,429 @@
mistplayers.webrtc = {
name: "WebRTC player",
mimes: ["webrtc"],
priority: MistUtil.object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype,source,MistVideo) {
if ((!("WebSocket" in window)) || (!("RTCPeerConnection" in window))) { return false; }
//check for http/https mismatch
if (location.protocol.replace(/^http/,"ws") != MistUtil.http.url.split(source.url.replace(/^http/,"ws")).protocol) {
MistVideo.log("HTTP/HTTPS mismatch for this source");
return false;
}
return true;
},
player: function(){}
};
var p = mistplayers.webrtc.player;
p.prototype = new MistPlayer();
p.prototype.build = function (MistVideo,callback) {
var me = this;
if ((typeof WebRTCBrowserEqualizerLoaded == "undefined") || (!WebRTCBrowserEqualizerLoaded)) {
//load it
var scripttag = document.createElement("script");
scripttag.src = MistVideo.urlappend(MistVideo.options.host+"/webrtc.js");
MistVideo.log("Retrieving webRTC browser equalizer code from "+scripttag.src);
document.head.appendChild(scripttag);
scripttag.onerror = function(){
MistVideo.showError("Failed to load webrtc browser equalizer",{nextCombo:5});
}
scripttag.onload = function(){
me.build(MistVideo,callback);
}
return;
}
var video = document.createElement("video");
//apply options
var attrs = ["autoplay","loop","poster"];
for (var i in attrs) {
var attr = attrs[i];
if (MistVideo.options[attr]) {
video.setAttribute(attr,(MistVideo.options[attr] === true ? "" : MistVideo.options[attr]));
}
}
if (MistVideo.info.type == "live") {
video.loop = false;
}
if (MistVideo.options.controls == "stock") {
video.setAttribute("controls","");
}
video.setAttribute("crossorigin","anonymous");
this.setSize = function(size){
video.style.width = size.width+"px";
video.style.height = size.height+"px";
};
MistUtil.event.addListener(video,"loadeddata",correctSubtitleSync);
MistUtil.event.addListener(video,"seeked",correctSubtitleSync);
var seekoffset = 0;
var hasended = false;
this.listeners = {
on_connected: function() {
seekoffset = 0;
hasended = false;
this.webrtc.play();
},
on_disconnected: function() {
MistVideo.log("Websocket sent on_disconnect");
/*
If a VoD file ends, we receive an on_stop, but no on_disconnect
If a live stream ends, we receive an on_disconnect, but no on_stop
If MistOutWebRTC crashes, we receive an on_stop and then an on_disconnect
*/
if (hasended) {
MistVideo.showError("Connection to media server ended unexpectedly.");
}
video.pause();
},
on_answer_sdp: function (ev) {
if (!ev.result) {
MistVideo.showError("Failed to open stream.");
this.on_disconnected();
return;
}
MistVideo.log("SDP answer received");
},
on_time: function(ev) {
//timeupdate
var oldoffset = seekoffset;
seekoffset = ev.current*1e-3 - video.currentTime;
if (Math.abs(oldoffset - seekoffset) > 1) { correctSubtitleSync(); }
var d = (ev.end == 0 ? Infinity : ev.end*1e-3);
if (d != duration) {
duration = d;
MistUtil.event.send("durationchange",d,video);
}
},
on_seek: function(){
MistUtil.event.send("seeked",seekoffset,video);
video.play();
},
on_stop: function(){
MistVideo.log("Websocket sent on_stop");
MistUtil.event.send("ended",null,video);
hasended = true;
}
};
function WebRTCPlayer(){
this.peerConn = null;
this.localOffer = null;
this.isConnected = false;
var thisWebRTCPlayer = this;
this.on_event = function(ev) {
//if (ev.type != "on_time") { console.log(ev); }
switch (ev.type) {
case "on_connected": {
thisWebRTCPlayer.isConnected = true;
break;
}
case "on_answer_sdp": {
thisWebRTCPlayer.peerConn
.setRemoteDescription({ type: "answer", sdp: ev.answer_sdp })
.then(function(){}, function(err) {
console.error(err);
});
break;
}
case "on_disconnected": {
thisWebRTCPlayer.isConnected = false;
break;
}
}
if (ev.type in me.listeners) {
return me.listeners[ev.type].call(me,ev);
}
MistVideo.log("Unhandled WebRTC event "+ev.type+": "+JSON.stringify(ev));
return false;
};
this.connect = function(callback){
thisWebRTCPlayer.signaling = new WebRTCSignaling(thisWebRTCPlayer.on_event);
thisWebRTCPlayer.peerConn = new RTCPeerConnection();
thisWebRTCPlayer.peerConn.ontrack = function(ev) {
video.srcObject = ev.streams[0];
if (callback) { callback(); }
};
};
this.play = function(){
if (!this.isConnected) {
throw "Not connected, cannot play";
}
this.peerConn
.createOffer({
offerToReceiveAudio: true,
offerToReceiveVideo: true,
})
.then(function(offer){
thisWebRTCPlayer.localOffer = offer;
thisWebRTCPlayer.peerConn
.setLocalDescription(offer)
.then(function(){
thisWebRTCPlayer.signaling.sendOfferSDP(thisWebRTCPlayer.localOffer.sdp);
}, function(err){console.error(err)});
}, function(err){ throw err; });
};
this.stop = function(){
if (!this.isConnected) { throw "Not connected, cannot stop." }
this.signaling.send({type: "stop"});
};
this.seek = function(seekTime){
if (!this.isConnected) { return; }
this.signaling.send({type: "seek", "seek_time": seekTime*1e3});
};
this.pause = function(){
if (!this.isConnected) { throw "Not connected, cannot pause." }
this.signaling.send({type: "pause"});
};
this.setTrack = function(obj){
if (!this.isConnected) { throw "Not connected, cannot set track." }
obj.type = "tracks";
this.signaling.send(obj);
};
this.getStats = function(callback){
this.peerConn.getStats().then(function(d){
var output = {};
var entries = Array.from(d.entries());
for (var i in entries) {
var value = entries[i];
if (value[1].type == "inbound-rtp") {
output[value[0]] = value[1];
}
}
callback(output);
});
};
//input only
/*
this.sendVideoBitrate = function(bitrate) {
this.send({type: "video_bitrate", video_bitrate: bitrate});
};
*/
this.connect();
}
function WebRTCSignaling(onEvent){
this.ws = null;
this.ws = new WebSocket(MistVideo.source.url.replace(/^http/,"ws"));
this.ws.onopen = function() {
onEvent({type: "on_connected"});
};
this.ws.onmessage = function(e) {
try {
var cmd = JSON.parse(e.data);
onEvent(cmd);
}
catch (err) {
console.error("Failed to parse a response from MistServer",err,e.data);
}
};
/* See http://tools.ietf.org/html/rfc6455#section-7.4.1 */
this.ws.onclose = function(ev) {
switch (ev.code) {
default: {
onEvent({type: "on_disconnected"});
break;
}
}
}
this.sendOfferSDP = function(sdp) {
this.send({type: "offer_sdp", offer_sdp: sdp});
};
this.send = function(cmd) {
if (!this.ws) {
throw "Not initialized, cannot send "+JSON.stringify(cmd);
}
this.ws.send(JSON.stringify(cmd));
}
};
this.webrtc = new WebRTCPlayer();
this.api = {};
//override video duration
var duration;
Object.defineProperty(this.api,"duration",{
get: function(){ return duration; }
});
//override seeking
Object.defineProperty(this.api,"currentTime",{
get: function(){
return seekoffset + video.currentTime;
},
set: function(value){
seekoffset = value - video.currentTime;
video.pause();
me.webrtc.seek(value);
MistUtil.event.send("seeking",value,video);
}
});
//redirect properties
//using a function to make sure the "item" is in the correct scope
function reroute(item) {
Object.defineProperty(me.api,item,{
get: function(){ return video[item]; },
set: function(value){
return video[item] = value;
}
});
}
var list = [
"volume"
,"muted"
,"loop"
,"paused",
,"error"
,"textTracks"
,"webkitDroppedFrameCount"
,"webkitDecodedFrameCount"
];
for (var i in list) {
reroute(list[i]);
}
//redirect methods
function redirect(item) {
if (item in video) {
me.api[item] = function(){
return video[item].call(video,arguments);
};
}
}
var list = ["load","getVideoPlaybackQuality"];
for (var i in list) {
redirect(list[i]);
}
//redirect play
me.api.play = function(){
if (me.api.currentTime) {
if ((!me.webrtc.isConnected) || (me.webrtc.peerConn.iceConnectionState != "completed")) {
me.webrtc.connect(function(){
me.webrtc.seek(me.api.currentTime);
});
}
else {
me.webrtc.seek(me.api.currentTime);
}
}
else {
video.play();
}
};
//redirect pause
me.api.pause = function(){
video.pause();
try {
me.webrtc.pause();
}
catch (e) {}
MistUtil.event.send("paused",null,video);
};
me.api.setTracks = function(obj){
me.webrtc.setTrack(obj);
};
function correctSubtitleSync() {
if (!me.api.textTracks[0]) { return; }
var currentoffset = me.api.textTracks[0].currentOffset || 0;
if (Math.abs(seekoffset - currentoffset) < 1) { return; } //don't bother if the change is small
var newCues = [];
for (var i = me.api.textTracks[0].cues.length-1; i >= 0; i--) {
var cue = me.api.textTracks[0].cues[i];
me.api.textTracks[0].removeCue(cue);
if (!("orig" in cue)) {
cue.orig = {start:cue.startTime,end:cue.endTime};
}
cue.startTime = cue.orig.start - seekoffset;
cue.endTime = cue.orig.end - seekoffset;
newCues.push(cue);
}
for (var i in newCues) {
me.api.textTracks[0].addCue(newCues[i]);
}
me.api.textTracks[0].currentOffset = seekoffset;
}
me.api.setSubtitle = function(trackmeta) {
//remove previous subtitles
var tracks = video.getElementsByTagName("track");
for (var i = tracks.length - 1; i >= 0; i--) {
video.removeChild(tracks[i]);
}
if (trackmeta) { //if the chosen track exists
//add the new one
var track = document.createElement("track");
video.appendChild(track);
track.kind = "subtitles";
track.label = trackmeta.label;
track.srclang = trackmeta.lang;
track.src = trackmeta.src;
track.setAttribute("default","");
//correct timesync
track.onload = correctSubtitleSync;
}
};
//loop
MistUtil.event.addListener(video,"ended",function(){
if (me.api.loop) {
me.webrtc.connect();
}
});
if ("decodingIssues" in MistVideo.skin.blueprints) {
//get additional dev stats
var vars = ["nackCount","pliCount","packetsLost","packetsReceived","bytesReceived"];
for (var j in vars) {
me.api[vars[j]] = 0;
}
var f = function() {
MistVideo.timers.start(function(){
me.webrtc.getStats(function(d){
for (var i in d) {
for (var j in vars) {
if (vars[j] in d[i]) {
me.api[vars[j]] = d[i][vars[j]];
}
}
break;
}
});
f();
},1e3);
};
f();
}
me.api.unload = function(){
try {
me.webrtc.stop();
} catch (e) {}
};
callback(video);
};

View file

@ -1,2 +1,9 @@
#!/bin/bash #!/bin/bash
java -jar closure-compiler.jar --warning_level QUIET plugins/md5.js plugins/cattablesort.js mist.js > minified.js echo "Minimizing LSP..";
if [ "minified.js" -ot "plugins/md5.js" ] || [ "minified.js" -ot "plugins/plugins/cattablesort.js" ] || [ "minified.js" -ot "mist.js" ]; then
echo " Generating minified.js.."
java -jar closure-compiler.jar --warning_level QUIET plugins/md5.js plugins/cattablesort.js mist.js > minified.js
fi
echo "Done.";

View file

@ -593,6 +593,29 @@ input[type=radio] {
height: 1.1em; height: 1.1em;
width: 1.1em; width: 1.1em;
} }
.input_container .itemsettings {
margin-bottom: 1em;
margin-left: 1em;
}
.input_container .subitem {
font-size: 0.9em;
color: #777;
width: 25em;
min-width: auto;
display: flex;
flex-flow: row nowrap;
}
.input_container .subitem .itemdetails {
overflow: hidden;
min-width: auto;
flex-shrink: 1;
flex-grow: 1;
text-overflow: ellipsis;
white-space: nowrap;
}
.input_container .subitem:first-child button.move {
display: none;
}
.LTSonly { .LTSonly {
overflow: hidden; overflow: hidden;
@ -872,11 +895,32 @@ button.return:before {
max-width: 28em; max-width: 28em;
font-size: 0.5em; font-size: 0.5em;
} }
.mistvideo { .mistvideo * {
max-width: 90%; min-width: initial;
max-width: calc(100vw - 22em);
overflow: hidden;
} }
.mistvideo h3 {
color: inherit;
text-shadow: none;
}
.mistvideo table {
color: inherit;
box-shadow: none;
border: none;
margin: 0;
}
.mistvideo table tr {
background: none;
}
.mistvideo table td {
padding: 0;
}
.mistvideo button {
background-image: none;
}
.mistvideo .text {
font-size: inherit;
}
.mistvideo-container * { min-width: auto; }
.player_log { .player_log {
overflow-y: auto; overflow-y: auto;
max-height: 10em; max-height: 10em;
@ -898,6 +942,20 @@ button.return:before {
.logs .content > * { .logs .content > * {
margin-right: 0.5em; margin-right: 0.5em;
} }
.mistvideo-log table {
width: auto;
box-shadow: none;
border: none;
}
.mistvideo-log table tr {
background-color: transparent;
}
.mistvideo-container {
display: flex;
}
.mistvideo-column {
flex-direction: column;
}
@font-face { @font-face {
font-family: Latin Modern Sans; font-family: Latin Modern Sans;

View file

@ -1,223 +0,0 @@
var MD5=function(a){function c(a,c){var b,d,f,g,e;f=a&2147483648;g=c&2147483648;b=a&1073741824;d=c&1073741824;e=(a&1073741823)+(c&1073741823);return b&d?e^2147483648^f^g:b|d?e&1073741824?e^3221225472^f^g:e^1073741824^f^g:e^f^g}function d(a,b,d,f,g,e,j){a=c(a,c(c(b&d|~b&f,g),j));return c(a<<e|a>>>32-e,b)}function b(a,b,d,f,g,e,j){a=c(a,c(c(b&f|d&~f,g),j));return c(a<<e|a>>>32-e,b)}function e(a,b,d,f,g,e,j){a=c(a,c(c(b^d^f,g),j));return c(a<<e|a>>>32-e,b)}function h(a,b,d,f,g,e,j){a=c(a,c(c(d^(b|~f),
g),j));return c(a<<e|a>>>32-e,b)}function m(a){var c="",b="",d;for(d=0;3>=d;d++)b=a>>>8*d&255,b="0"+b.toString(16),c+=b.substr(b.length-2,2);return c}var g=[],q,o,l,u,f,j,i,k,g=a.replace(/\r\n/g,"\n"),a="";for(q=0;q<g.length;q++)o=g.charCodeAt(q),128>o?a+=String.fromCharCode(o):(127<o&&2048>o?a+=String.fromCharCode(o>>6|192):(a+=String.fromCharCode(o>>12|224),a+=String.fromCharCode(o>>6&63|128)),a+=String.fromCharCode(o&63|128));g=a;a=g.length;q=a+8;o=16*((q-q%64)/64+1);l=Array(o-1);for(f=u=0;f<a;)q=
(f-f%4)/4,u=8*(f%4),l[q]|=g.charCodeAt(f)<<u,f++;q=(f-f%4)/4;l[q]|=128<<8*(f%4);l[o-2]=a<<3;l[o-1]=a>>>29;g=l;f=1732584193;j=4023233417;i=2562383102;k=271733878;for(a=0;a<g.length;a+=16)q=f,o=j,l=i,u=k,f=d(f,j,i,k,g[a+0],7,3614090360),k=d(k,f,j,i,g[a+1],12,3905402710),i=d(i,k,f,j,g[a+2],17,606105819),j=d(j,i,k,f,g[a+3],22,3250441966),f=d(f,j,i,k,g[a+4],7,4118548399),k=d(k,f,j,i,g[a+5],12,1200080426),i=d(i,k,f,j,g[a+6],17,2821735955),j=d(j,i,k,f,g[a+7],22,4249261313),f=d(f,j,i,k,g[a+8],7,1770035416),
k=d(k,f,j,i,g[a+9],12,2336552879),i=d(i,k,f,j,g[a+10],17,4294925233),j=d(j,i,k,f,g[a+11],22,2304563134),f=d(f,j,i,k,g[a+12],7,1804603682),k=d(k,f,j,i,g[a+13],12,4254626195),i=d(i,k,f,j,g[a+14],17,2792965006),j=d(j,i,k,f,g[a+15],22,1236535329),f=b(f,j,i,k,g[a+1],5,4129170786),k=b(k,f,j,i,g[a+6],9,3225465664),i=b(i,k,f,j,g[a+11],14,643717713),j=b(j,i,k,f,g[a+0],20,3921069994),f=b(f,j,i,k,g[a+5],5,3593408605),k=b(k,f,j,i,g[a+10],9,38016083),i=b(i,k,f,j,g[a+15],14,3634488961),j=b(j,i,k,f,g[a+4],20,3889429448),
f=b(f,j,i,k,g[a+9],5,568446438),k=b(k,f,j,i,g[a+14],9,3275163606),i=b(i,k,f,j,g[a+3],14,4107603335),j=b(j,i,k,f,g[a+8],20,1163531501),f=b(f,j,i,k,g[a+13],5,2850285829),k=b(k,f,j,i,g[a+2],9,4243563512),i=b(i,k,f,j,g[a+7],14,1735328473),j=b(j,i,k,f,g[a+12],20,2368359562),f=e(f,j,i,k,g[a+5],4,4294588738),k=e(k,f,j,i,g[a+8],11,2272392833),i=e(i,k,f,j,g[a+11],16,1839030562),j=e(j,i,k,f,g[a+14],23,4259657740),f=e(f,j,i,k,g[a+1],4,2763975236),k=e(k,f,j,i,g[a+4],11,1272893353),i=e(i,k,f,j,g[a+7],16,4139469664),
j=e(j,i,k,f,g[a+10],23,3200236656),f=e(f,j,i,k,g[a+13],4,681279174),k=e(k,f,j,i,g[a+0],11,3936430074),i=e(i,k,f,j,g[a+3],16,3572445317),j=e(j,i,k,f,g[a+6],23,76029189),f=e(f,j,i,k,g[a+9],4,3654602809),k=e(k,f,j,i,g[a+12],11,3873151461),i=e(i,k,f,j,g[a+15],16,530742520),j=e(j,i,k,f,g[a+2],23,3299628645),f=h(f,j,i,k,g[a+0],6,4096336452),k=h(k,f,j,i,g[a+7],10,1126891415),i=h(i,k,f,j,g[a+14],15,2878612391),j=h(j,i,k,f,g[a+5],21,4237533241),f=h(f,j,i,k,g[a+12],6,1700485571),k=h(k,f,j,i,g[a+3],10,2399980690),
i=h(i,k,f,j,g[a+10],15,4293915773),j=h(j,i,k,f,g[a+1],21,2240044497),f=h(f,j,i,k,g[a+8],6,1873313359),k=h(k,f,j,i,g[a+15],10,4264355552),i=h(i,k,f,j,g[a+6],15,2734768916),j=h(j,i,k,f,g[a+13],21,1309151649),f=h(f,j,i,k,g[a+4],6,4149444226),k=h(k,f,j,i,g[a+11],10,3174756917),i=h(i,k,f,j,g[a+2],15,718787259),j=h(j,i,k,f,g[a+9],21,3951481745),f=c(f,q),j=c(j,o),i=c(i,l),k=c(k,u);return(m(f)+m(j)+m(i)+m(k)).toLowerCase()};(function(a){a.fn.stupidtable=function(){a(this).on("click","thead th",function(){a(this).stupidsort()})};a.fn.stupidsort=function(){function c(c){var b=0,d;a(c).children("td,th").each(function(){if(b==q)return d=a(this),!1;var c=a(this).attr("colspan");b+=c?Number(c):1});c="undefined"!=typeof d.data("sort-value")?d.data("sort-value"):"undefined"!=typeof d.attr("data-sort-value")?d.attr("data-sort-value"):d.text();switch(m){case "string":case "string-ins":c=String(c).toLowerCase();break;case "int":c=
parseInt(Number(c));break;case "float":c=Number(c)}return c}var d=a(this),b=d.closest("table"),e=b.children("tbody"),h=e.children("tr"),m=d.attr("data-sort-type");if(m){var g=!0;d.hasClass("sorting-asc")&&(g=!1);var q=0;d.prevAll().each(function(){var c=a(this).attr("colspan");q+=c?Number(c):1});h.sort(function(a,b){var d=g?1:-1,a=c(a),b=c(b);return a>b?1*d:a<b?-1*d:0});e.append(h);b.find("thead th").removeClass("sorting-asc").removeClass("sorting-desc");d.addClass(g?"sorting-asc":"sorting-desc")}}})(jQuery);$(function(){UI.elements={menu:$("nav > .menu"),main:$("main"),header:$("header"),connection:{status:$("#connection"),user_and_host:$("#user_and_host"),msg:$("#message")}};UI.buildMenu();UI.stored.getOpts();try{if("mistLogin"in sessionStorage){var a=JSON.parse(sessionStorage.mistLogin);mist.user.name=a.name;mist.user.password=a.password;mist.user.host=a.host}}catch(c){}location.hash&&(a=decodeURIComponent(location.hash).substring(1).split("@")[0].split("&"),mist.user.name=a[0],a[1]&&(mist.user.host=
a[1]));mist.send(function(){$(window).trigger("hashchange")},{},{timeout:5,hide:!0});var d=0;$("body > div.filler").on("scroll",function(){var a=$(this).scrollLeft();a!=d&&UI.elements.header.css("margin-right",-1*a+"px");d=a})});$(window).on("hashchange",function(){var a=decodeURIComponent(location.hash).substring(1).split("@");a[1]||(a[1]="");a=a[1].split("&");""==a[0]&&(a[0]="Overview");UI.showTab(a[0],a[1])});
var otherhost={host:!1,https:!1},UI={debug:!1,elements:{},stored:{getOpts:function(){var a=localStorage.stored;a&&(a=JSON.parse(a));$.extend(!0,this.vars,a);return this.vars},saveOpt:function(a,c){this.vars[a]=c;localStorage.stored=JSON.stringify(this.vars);return this.vars},vars:{helpme:!0}},interval:{clear:function(){"undefined"!=typeof this.opts&&(clearInterval(this.opts.id),delete this.opts)},set:function(a,c){this.opts&&log("[interval]","Set called on interval, but an interval is already active.");
this.opts={delay:c,callback:a};this.opts.id=setInterval(a,c)}},returnTab:["Overview"],countrylist:{AF:"Afghanistan",AX:"&Aring;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&ocirc;te d'Ivoire",HR:"Croatia",CU:"Cuba",CW:"Cura&ccedil;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&eacute;union",RO:"Romania",RU:"Russian Federation",RW:"Rwanda",BL:"Saint Barth&eacute;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(a,c){$tooltip=this.element;$.contains(document.body,$tooltip[0])||$("body").append($tooltip);$tooltip.html(c);
clearTimeout(this.hiding);delete this.hiding;var d=$(document).height()-$tooltip.outerHeight(),b=$(document).width()-$tooltip.outerWidth();$tooltip.css("left",Math.min(a.pageX+10,b-10));$tooltip.css("top",Math.min(a.pageY+25,d-10));$tooltip.show().addClass("show")},hide:function(){$tooltip=this.element;$tooltip.removeClass("show");this.hiding=setTimeout(function(){$tooltip.hide()},500)},element:$("<div>").attr("id","tooltip")},humanMime:function(a){var c=!1;switch(a){case "html5/application/vnd.apple.mpegurl":c=
"HLS";break;case "html5/video/mp4":c="MP4";break;case "dash/video/mp4":c="DASH";break;case "flash/11":c="HDS";break;case "flash/10":c="RTMP";break;case "flash/7":c="Progressive";break;case "html5/audio/mp3":c="MP3";break;case "html5/video/mp2t":c="TS";break;case "html5/application/vnd.ms-ss":c="Smooth";break;case "html5/text/vtt":c="VTT Subtitles";break;case "html5/text/plain":c="SRT Subtitles";break;case "html5/text/javascript":c="JSON Subtitles"}return c},popup:{element:null,show:function(a){this.element=
$("<div>").attr("id","popup").append($("<button>").text("Close").addClass("close").click(function(){UI.popup.element.fadeOut("fast",function(){UI.popup.element.remove();UI.popup.element=null})})).append(a);$("body").append(this.element)}},menu:[{Overview:{},Protocols:{},Streams:{hiddenmenu:{Edit:{},Preview:{},Embed:{}}},Push:{LTSonly:!0},Triggers:{LTSonly:!1},Logs:{},Statistics:{},"Server Stats":{}},{Disconnect:{classes:["red"]}},{Guides:{link:"http://mistserver.org/documentation#Userdocs"},Tools:{submenu:{"Release notes":{link:"http://mistserver.org/documentation#Devdocs"},
"Mist Shop":{link:"http://mistserver.org/products"},"Email for Help":{}}}}],buildMenu:function(){function a(a,c){var b=$("<a>").addClass("button");b.html($("<span>").addClass("plain").text(a)).append($("<span>").addClass("highlighted").text(a));for(var d in c.classes)b.addClass(c.classes[d]);"LTSonly"in c&&b.addClass("LTSonly");"link"in c?b.attr("href",c.link).attr("target","_blank"):"submenu"in c||b.click(function(c){$(this).closest(".menu").hasClass("hide")||(UI.navto(a),c.stopPropagation())});
return b}var c=UI.elements.menu,d;for(d in UI.menu){0<d&&c.append($("<br>"));for(var b in UI.menu[d]){var e=UI.menu[d][b],h=a(b,e);c.append(h);if("submenu"in e){var m=$("<span>").addClass("submenu");h.addClass("arrowdown").append(m);for(var g in e.submenu)m.append(a(g,e.submenu[g]))}else if("hiddenmenu"in e)for(g in m=$("<span>").addClass("hiddenmenu"),h.append(m),e.hiddenmenu)m.append(a(g,e.hiddenmenu[g]))}}d=$("<div>").attr("id","ih_button").text("?").click(function(){$("body").toggleClass("helpme");
UI.stored.saveOpt("helpme",$("body").hasClass("helpme"))}).attr("title","Click to toggle the display of integrated help");UI.stored.getOpts().helpme&&$("body").addClass("helpme");c.after(d).after($("<div>").addClass("separator"))},findInput:function(a){return this.findInOutput("inputs",a)},findOutput:function(a){return this.findInOutput("connectors",a)},findInOutput:function(a,c){if("capabilities"in mist.data){var d=!1,b=mist.data.capabilities[a];c in b&&(d=b[c]);c+".exe"in b&&(d=b[c+".exe"]);return d}throw"Request capabilities first";
},buildUI:function(a){var c=$("<div>").addClass("input_container"),d;for(d in a){var b=a[d];if(b instanceof jQuery)c.append(b);else if("help"==b.type){var e=$("<span>").addClass("text_container").append($("<span>").addClass("description").append(b.help));c.append(e);if("classes"in b)for(var h in b.classes)e.addClass(b.classes[h])}else if("text"==b.type)c.append($("<span>").addClass("text_container").append($("<span>").addClass("text").append(b.text)));else if("custom"==b.type)c.append(b.custom);else if("buttons"==
b.type)for(h in e=$("<span>").addClass("button_container").on("keydown",function(a){a.stopPropagation()}),"css"in b&&e.css(b.css),c.append(e),b.buttons){var m=b.buttons[h],g=$("<button>").text(m.label).data("opts",m);"css"in m&&g.css(m.css);if("classes"in m)for(var q in m.classes)g.addClass(m.classes[q]);e.append(g);switch(m.type){case "cancel":g.addClass("cancel").click(m["function"]);break;case "save":g.addClass("save").click(function(){var a=$(this).closest(".input_container"),c=!1;a.find(".hasValidate").each(function(){if(c=
$(this).data("validate")(this,!0))return!1});c||(a.find(".isSetting").each(function(){var a=$(this).getval(),c=$(this).data("pointer");if(""==a)if("default"in $(this).data("opts"))a=$(this).data("opts")["default"];else return c.main[c.index]=null,!0;c.main[c.index]=a}),(a=$(this).data("opts")["function"])&&a(this))});break;default:g.click(m["function"])}}else{m=$("<label>").addClass("UIelement");c.append(m);"css"in b&&m.css(b.css);m.append($("<span>").addClass("label").html("label"in b?b.label+":":
""));g=$("<span>").addClass("field_container");m.append(g);switch(b.type){case "password":e=$("<input>").attr("type","password");break;case "int":e=$("<input>").attr("type","number");"min"in b&&e.attr("min",b.min);"max"in b&&e.attr("max",b.min);"validate"in b?b.validate.push("int"):b.validate=["int"];break;case "span":e=$("<span>");break;case "debug":b.select=[["","Default"],[0,"0 - All debugging messages disabled"],[1,"1 - Messages about failed operations"],[2,"2 - Previous level, and error messages"],
[3,"3 - Previous level, and warning messages"],[4,"4 - Previous level, and status messages for development"],[5,"5 - Previous level, and more status messages for development"],[6,"6 - Previous level, and verbose debugging messages"],[7,"7 - Previous level, and very verbose debugging messages"],[8,"8 - Report everything in extreme detail"],[9,"9 - Report everything in insane detail"],[10,"10 - All messages enabled"]];case "select":e=$("<select>");for(h in b.select){var o=$("<option>");"string"==typeof b.select[h]?
o.text(b.select[h]):o.val(b.select[h][0]).text(b.select[h][1]);e.append(o)}break;case "textarea":e=$("<textarea>").on("keydown",function(a){a.stopPropagation()});break;case "checkbox":e=$("<input>").attr("type","checkbox");break;case "hidden":e=$("<input>").attr("type","hidden");m.hide();break;case "email":e=$("<input>").attr("type","email").attr("autocomplete","on").attr("required","");break;case "browse":e=$("<input>").attr("type","text");"filetypes"in b&&e.data("filetypes",b.filetypes);break;case "geolimited":case "hostlimited":e=
$("<input>").attr("type","hidden");break;case "radioselect":e=$("<div>").addClass("radioselect");for(d in b.radioselect){var l=$("<input>").attr("type","radio").val(b.radioselect[d][0]).attr("name",b.label);("LTSonly"in b&&!mist.data.LTS||b.readonly)&&l.prop("disabled",!0);o=$("<label>").append(l).append($("<span>").html(b.radioselect[d][1]));e.append(o);if(2<b.radioselect[d].length)for(h in l=$("<select>").change(function(){$(this).parent().find("input[type=radio]:enabled").prop("checked","true")}),
o.append(l),("LTSonly"in b&&!mist.data.LTS||b.readonly)&&l.prop("disabled",!0),b.radioselect[d][2])o=$("<option>"),l.append(o),b.radioselect[d][2][h]instanceof Array?o.val(b.radioselect[d][2][h][0]).html(b.radioselect[d][2][h][1]):o.html(b.radioselect[d][2][h])}break;case "checklist":e=$("<div>").addClass("checkcontainer");$controls=$("<div>").addClass("controls");$checklist=$("<div>").addClass("checklist");e.append($checklist);for(d in b.checklist)"string"==typeof b.checklist[d]&&(b.checklist[d]=
[b.checklist[d],b.checklist[d]]),$checklist.append($("<label>").text(b.checklist[d][1]).prepend($("<input>").attr("type","checkbox").attr("name",b.checklist[d][0])));break;case "DOMfield":e=b.DOMfield;break;case "unix":e=$("<input>").attr("type","datetime-local").attr("step",1);b.unit=$("<button>").text("Now").click(function(){$(this).closest(".field_container").find(".field").setval((new Date).getTime()/1E3)});break;case "selectinput":e=$("<div>").addClass("selectinput");l=$("<select>");e.append(l);
l.data("input",!1);"LTSonly"in b&&!mist.data.LTS&&l.prop("disabled",!0);for(d in b.selectinput)o=$("<option>"),l.append(o),"string"==typeof b.selectinput[d]?o.text(b.selectinput[d]):(o.text(b.selectinput[d][1]),"string"==typeof b.selectinput[d][0]?o.val(b.selectinput[d][0]):(o.val("CUSTOM"),l.data("input")||l.data("input",UI.buildUI([b.selectinput[d][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=$("<input>").attr("type","text").addClass("listitem");("LTSonly"in b&&!mist.data.LTS||b.readonly)&&a.prop("disabled",!0);var c=function(b){$(this).is(":last-child")?""!=$(this).val()?$(this).after(a.clone().keyup(c).val("")):8==b.which&&$(this).prev().focus():""==$(this).val()&&($(this).next().focus(),$(this).remove())};a.keyup(c);return a});e.append(e.data("newitem"));
break;default:e=$("<input>").attr("type","text")}e.addClass("field").data("opts",b);"pointer"in b&&e.attr("name",b.pointer.index);g.append(e);if("classes"in b)for(h in b.classes)e.addClass(b.classes[h]);"placeholder"in b&&e.attr("placeholder",b.placeholder);"default"in b&&e.attr("placeholder",b["default"]);"unit"in b&&g.append($("<span>").addClass("unit").html(b.unit));"readonly"in b&&(e.attr("readonly","readonly"),e.click(function(){$(this).select()}));"qrcode"in b&&g.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()),c=$("<div>").addClass("qrcode");UI.popup.show($("<span>").addClass("qr_container").append($("<p>").text(a)).append(c));c.qrcode({text:a,size:Math.min(c.width(),c.height())})})));"clipboard"in b&&document.queryCommandSupported("copy")&&g.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()),
c=document.createElement("textarea");c.value=a;document.body.appendChild(c);c.select();var b=false;try{b=document.execCommand("copy")}catch(d){}if(b){$(this).text("Copied to clipboard!");document.body.removeChild(c);var g=$(this);setTimeout(function(){g.text("Copy")},5E3)}else{document.body.removeChild(c);alert("Failed to copy:\n"+a)}})));"rows"in b&&e.attr("rows",b.rows);"LTSonly"in b&&!mist.data.LTS&&(g.addClass("LTSonly"),e.prop("disabled",!0));switch(b.type){case "browse":l=$("<div>").addClass("grouper").append(m);
c.append(l);l=$("<button>").text("Browse").on("keydown",function(a){a.stopPropagation()});g.append(l);l.click(function(){function a(c){m.text("Loading..");mist.send(function(a){h.text(a.browse.path[0]);mist.data.LTS&&d.setval(a.browse.path[0]+"/");m.html(l.clone(true).text("..").attr("title","Folder up"));if(a.browse.subdirectories){a.browse.subdirectories.sort();for(var c in a.browse.subdirectories){var e=a.browse.subdirectories[c];m.append(l.clone(true).attr("title",h.text()+q+e).text(e))}}if(a.browse.files){a.browse.files.sort();
for(c in a.browse.files){var e=a.browse.files[c],f=h.text()+q+e,e=$("<a>").text(e).addClass("file").attr("title",f);m.append(e);if(o){var u=true,I;for(I in o)if(typeof o[I]!="undefined"&&mist.inputMatch(o[I],f)){u=false;break}u&&e.hide()}e.click(function(){var a=$(this).attr("title");d.setval(a).removeAttr("readonly").css("opacity",1);g.show();b.remove()})}}},{browse:c})}var c=$(this).closest(".grouper"),b=$("<div>").addClass("browse_container"),d=c.find(".field").attr("readonly","readonly").css("opacity",
0.5),g=$(this),e=$("<button>").text("Stop browsing").click(function(){g.show();b.remove();d.removeAttr("readonly").css("opacity",1)}),h=$("<span>").addClass("field"),m=$("<div>").addClass("browse_contents"),l=$("<a>").addClass("folder"),o=d.data("filetypes");c.append(b);b.append($("<label>").addClass("UIelement").append($("<span>").addClass("label").text("Current folder:")).append($("<span>").addClass("field_container").append(h).append(e))).append(m);var q="/";mist.data.config.version.indexOf("indows")>
-1&&(q="\\");l.click(function(){var c=h.text()+q+$(this).text();a(c)});c=d.getval();e=c.split("://");e.length>1&&(c=e[0]=="file"?e[1]:"");c=c.split(q);c.pop();c=c.join(q);g.hide();a(c)});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(b.type){case "geolimited":l.prototype=$("<select>").append($("<option>").val("").text("[Select a country]"));
for(d in UI.countrylist)l.prototype.append($("<option>").val(d).html(UI.countrylist[d]));break;case "hostlimited":l.prototype=$("<input>").attr("type","text").attr("placeholder","type a host")}l.prototype.on("change keyup",function(){$(this).closest(".field_container").data("subUI").blackwhite.trigger("change")});l.blackwhite.change(function(){var a=$(this).closest(".field_container").data("subUI"),c=[],b=false;a.values.children().each(function(){b=$(this).val();b!=""?c.push(b):$(this).remove()});
a.values.append(a.prototype.clone(true));c.length>0?a.field.val($(this).val()+c.join(" ")):a.field.val("");a.field.trigger("change")});"LTSonly"in b&&!mist.data.LTS&&(l.blackwhite.prop("disabled",!0),l.prototype.prop("disabled",!0));l.values.append(l.prototype.clone(!0));g.data("subUI",l).addClass("limit_list").append(l.blackwhite).append(l.values)}"pointer"in b&&(e.data("pointer",b.pointer).addClass("isSetting"),b.pointer.main&&(l=b.pointer.main[b.pointer.index],"undefined"!=l&&e.setval(l)));"value"in
b&&e.setval(b.value);if("datalist"in b)for(d in l="datalist_"+d+MD5(e[0].outerHTML),e.attr("list",l),l=$("<datalist>").attr("id",l),g.append(l),b.datalist)l.append($("<option>").val(b.datalist[d]));g=$("<span>").addClass("help_container");m.append(g);"help"in b&&(g.append($("<span>").addClass("ih_balloon").html(b.help)),e.on("focus mouseover",function(){$(this).closest("label").addClass("active")}).on("blur mouseout",function(){$(this).closest("label").removeClass("active")}));if("validate"in b){m=
[];for(h in b.validate){l=b.validate[h];if("function"!=typeof l)switch(l){case "required":l=function(a){return a==""||a==null?{msg:"This is a required field.",classes:["red"]}:false};break;case "int":l=function(a,c){var b=$(c).data("opts");if(!$(c)[0].validity.valid){var d=[];"min"in b&&d.push(" greater than or equal to "+b.min);"max"in b&&d.push(" smaller than or equal to "+b.max);return{msg:"Please enter an integer"+d.join(" and")+".",classes:["red"]}}if(parseInt(Number(a))!=a)return{msg:"Please enter an integer.",
classes:["red"]}};break;case "streamname":l=function(a,c){if(!isNaN(a.charAt(0)))return{msg:"The first character may not be a number.",classes:["red"]};if(a.toLowerCase()!=a)return{msg:"Uppercase letters are not allowed.",classes:["red"]};if(a.replace(/[^\da-z_]/g,"")!=a)return{msg:"Special characters (except for underscores) are not allowed.",classes:["red"]};if("streams"in mist.data&&a in mist.data.streams&&$(c).data("pointer").main.name!=a)return{msg:"This streamname already exists.<br>If you want to edit an existing stream, please click edit on the the streams tab.",
classes:["red"]}};break;default:l=function(){}}m.push(l)}e.data("validate_functions",m).data("help_container",g).data("validate",function(a,c){var b=$(a).getval(),d=$(a).data("validate_functions"),e=$(a).data("help_container");e.find(".err_balloon").remove();for(var g in d){var h=d[g](b,a);if(h){$err=$("<span>").addClass("err_balloon").html(h.msg);for(var m in h.classes)$err.addClass(h.classes[m]);e.prepend($err);c&&$(a).focus();return typeof h=="object"&&"break"in h?h["break"]:true}}return false}).addClass("hasValidate").on("change keyup",
function(){$(this).data("validate")($(this))});""!=e.getval()&&e.trigger("change")}"function"in b&&(e.on("change keyup",b["function"]),e.trigger("change"))}}c.on("keydown",function(a){switch(a.which){case 13:$(this).find("button.save").first().trigger("click");break;case 27:$(this).find("button.cancel").first().trigger("click")}});return c},buildVheaderTable:function(a){var c=$("<table>").css("margin","0.2em"),d=$("<tr>").addClass("header").append($("<td>").addClass("vheader").attr("rowspan",a.labels.length+
1).append($("<span>").text(a.vheader))),b=[];d.append($("<td>"));for(var e in a.labels)b.push($("<tr>").append($("<td>").html(""==a.labels[e]?"&nbsp;":a.labels[e]+":")));for(var h in a.content)for(e in d.append($("<td>").html(a.content[h].header)),a.content[h].body)b[e].append($("<td>").html(a.content[h].body[e]));c.append($("<tbody>").append(d).append(b));return c},plot:{addGraph:function(a,c){var d={id:a.id,xaxis:a.xaxis,datasets:[],elements:{cont:$("<div>").addClass("graph"),plot:$("<div>").addClass("plot"),
legend:$("<div>").addClass("legend").attr("draggable","true")}};UI.draggable(d.elements.legend);d.elements.cont.append(d.elements.plot).append(d.elements.legend);c.append(d.elements.cont);return d},go:function(a){if(!(1>Object.keys(a).length)){var c={totals:[],clients:[]},d;for(d in a)for(var b in a[d].datasets){var e=a[d].datasets[b];switch(e.datatype){case "clients":case "upbps":case "downbps":switch(e.origin[0]){case "total":c.totals.push({fields:[e.datatype],end:-15});break;case "stream":c.totals.push({fields:[e.datatype],
streams:[e.origin[1]],end:-15});break;case "protocol":c.totals.push({fields:[e.datatype],protocols:[e.origin[1]],end:-15})}break;case "cpuload":case "memload":c.capabilities={}}}0==c.totals.length&&delete c.totals;0==c.clients.length&&delete c.clients;mist.send(function(){for(var c in a){var b=a[c];if(1>b.datasets.length){b.elements.plot.html("");b.elements.legend.html("");break}switch(b.xaxis){case "time":var d=[];b.yaxes={};var e=[],o;for(o in b.datasets){var l=b.datasets[o];l.display&&(l.getdata(),
l.yaxistype in b.yaxes||(d.push(UI.plot.yaxes[l.yaxistype]),b.yaxes[l.yaxistype]=d.length),l.yaxis=b.yaxes[l.yaxistype],e.push(l))}d[0]&&(d[0].color=0);b.plot=$.plot(b.elements.plot,e,{legend:{show:!1},xaxis:UI.plot.xaxes[b.xaxis],yaxes:d,grid:{hoverable:!0,borderWidth:{top:0,right:0,bottom:1,left:1},color:"black",backgroundColor:{colors:["rgba(0,0,0,0)","rgba(0,0,0,0.025)"]}},crosshair:{mode:"x"}});d=$("<table>").addClass("legend-list").addClass("nolay").html($("<tr>").html($("<td>").html($("<h3>").text(b.id))).append($("<td>").css("padding-right",
"2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").data("opts",b).text("X").addClass("close").click(function(){var c=$(this).data("opts");if(confirm("Are you sure you want to remove "+c.id+"?")){c.elements.cont.remove();var b=$(".graph_ids option:contains("+c.id+")"),d=b.parent();b.remove();UI.plot.del(c.id);delete a[c.id];d.trigger("change");UI.plot.go(a)}}))));b.elements.legend.html(d);var u=function(a){var c=b.elements.legend.find(".value"),d=1;if(typeof a==
"undefined")c.eq(0).html("Latest:");else{var e=b.plot.getXAxes()[0],a=Math.min(e.max,a),a=Math.max(e.min,a);c.eq(0).html(UI.format.time(a/1E3))}for(var g in b.datasets){var f="&nbsp;";if(b.datasets[g].display){var e=UI.plot.yaxes[b.datasets[g].yaxistype].tickFormatter,h=b.datasets[g].data;if(a)for(var l in h){if(h[l][0]==a){f=e(h[l][1]);break}if(h[l][0]>a){if(l!=0){f=h[l];h=h[l-1];f=e(f[1]+(a-f[0])*(h[1]-f[1])/(h[0]-f[0]))}break}}else f=e(b.datasets[g].data[b.datasets[g].data.length-1][1])}c.eq(d).html(f);
d++}};b.plot.getOptions();for(o in b.datasets)e=$("<input>").attr("type","checkbox").data("index",o).data("graph",b).click(function(){var a=$(this).data("graph");$(this).is(":checked")?a.datasets[$(this).data("index")].display=true:a.datasets[$(this).data("index")].display=false;var c={};c[a.id]=a;UI.plot.go(c)}),b.datasets[o].display&&e.attr("checked","checked"),d.append($("<tr>").html($("<td>").html($("<label>").html(e).append($("<div>").addClass("series-color").css("background-color",b.datasets[o].color)).append(b.datasets[o].label))).append($("<td>").css("padding-right",
"2em").css("text-align","right").html($("<span>").addClass("value")).append($("<button>").text("X").addClass("close").data("index",o).data("graph",b).click(function(){var c=$(this).data("index"),b=$(this).data("graph");if(confirm("Are you sure you want to remove "+b.datasets[c].label+" from "+b.id+"?")){b.datasets.splice(c,1);if(b.datasets.length==0){b.elements.cont.remove();var c=$(".graph_ids option:contains("+b.id+")"),d=c.parent();c.remove();d.trigger("change");UI.plot.del(b.id);delete a[b.id];
UI.plot.go(a)}else{UI.plot.save(b);c={};c[b.id]=b;UI.plot.go(c)}}}))));u();var f=!1;b.elements.plot.on("plothover",function(a,c,b){if(c.x!=f){u(c.x);f=c.x}if(b){a=$("<span>").append($("<h3>").text(b.series.label).prepend($("<div>").addClass("series-color").css("background-color",b.series.color))).append($("<table>").addClass("nolay").html($("<tr>").html($("<td>").text("Time:")).append($("<td>").html(UI.format.dateTime(b.datapoint[0]/1E3,"long")))).append($("<tr>").html($("<td>").text("Value:")).append($("<td>").html(b.series.yaxis.tickFormatter(b.datapoint[1],
b.series.yaxis)))));UI.tooltip.show(c,a.children())}else UI.tooltip.hide()}).on("mouseout",function(){u()})}}},c)}},save:function(a){var c={id:a.id,xaxis:a.xaxis,datasets:[]},d;for(d in a.datasets)c.datasets.push({origin:a.datasets[d].origin,datatype:a.datasets[d].datatype});a=mist.stored.get().graphs||{};a[c.id]=c;mist.stored.set("graphs",a)},del:function(a){var c=mist.stored.get().graphs||{};delete c[a];mist.stored.set("graphs",c)},datatype:{getOptions:function(a){var c=$.extend(!0,{},UI.plot.datatype.templates.general),
d=$.extend(!0,{},UI.plot.datatype.templates[a.datatype]),a=$.extend(!0,d,a),a=$.extend(!0,c,a);switch(a.origin[0]){case "total":switch(a.datatype){case "cpuload":case "memload":break;default:a.label+=" (total)"}break;case "stream":case "protocol":a.label+=" ("+a.origin[1]+")"}var c=[],b;for(b in a.basecolor)d=a.basecolor[b],d+=50*(0.5-Math.random()),d=Math.round(d),d=Math.min(255,Math.max(0,d)),c.push(d);a.color="rgb("+c.join(",")+")";return a},templates:{general:{display:!0,datatype:"general",label:"",
yaxistype:"amount",data:[],lines:{show:!0},points:{show:!1},getdata:function(){var a=mist.data.totals["stream"==this.origin[0]?this.origin[1]:"all_streams"]["protocol"==this.origin[0]?this.origin[1]:"all_protocols"][this.datatype];return this.data=a}},cpuload:{label:"CPU use",yaxistype:"percentage",basecolor:[237,194,64],cores:1,getdata:function(){var a=!1,c;for(c in this.data)this.data[c][0]<1E3*(mist.data.config.time-600)&&(a=c);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time,
mist.data.capabilities.cpu_use/10]);return this.data}},memload:{label:"Memory load",yaxistype:"percentage",basecolor:[175,216,248],getdata:function(){var a=!1,c;for(c in this.data)this.data[c][0]<1E3*(mist.data.config.time-600)&&(a=c);!1!==a&&this.data.splice(0,Number(a)+1);this.data.push([1E3*mist.data.config.time,mist.data.capabilities.load.memory]);return this.data}},clients:{label:"Connections",basecolor:[203,75,75]},upbps:{label:"Bandwidth up",yaxistype:"bytespersec",basecolor:[77,167,77]},downbps:{label:"Bandwidth down",
yaxistype:"bytespersec",basecolor:[148,64,237]}}},yaxes:{percentage:{name:"percentage",color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.addUnit(UI.format.number(a),"%")},tickLength:0,min:0,max:100},amount:{name:"amount",color:"black",tickColor:0,tickDecimals:0,tickFormatter:function(a){return UI.format.number(a)},tickLength:0,min:0},bytespersec:{name:"bytespersec",color:"black",tickColor:0,tickDecimals:1,tickFormatter:function(a){return UI.format.bytes(a,!0)},tickLength:0,
ticks:function(a){var c=0.3*Math.sqrt($(".graph").first().height()),c=(a.max-a.min)/c,d=Math.floor(Math.log(Math.abs(c))/Math.log(1024)),b=c/Math.pow(1024,d),e=-Math.floor(Math.log(b)/Math.LN10),h=a.tickDecimals;null!=h&&e>h&&(e=h);var m=Math.pow(10,-e),b=b/m,g;if(1.5>b)g=1;else if(3>b){if(g=2,2.25<b&&(null==h||e+1<=h))g=2.5,++e}else g=7.5>b?5:10;g=g*m*Math.pow(1024,d);null!=a.minTickSize&&g<a.minTickSize&&(g=a.minTickSize);a.delta=c;a.tickDecimals=Math.max(0,null!=h?h:e);a.tickSize=g;c=[];d=a.tickSize*
Math.floor(a.min/a.tickSize);e=0;h=Number.NaN;do m=h,h=d+e*a.tickSize,c.push(h),++e;while(h<a.max&&h!=m);return c},min:0}},xaxes:{time:{name:"time",mode:"time",timezone:"browser",ticks:5}}},draggable:function(a){a.attr("draggable",!0);a.on("dragstart",function(a){$(this).css("opacity",0.4).data("dragstart",{click:{x:a.originalEvent.pageX,y:a.originalEvent.pageY},ele:{x:this.offsetLeft,y:this.offsetTop}})}).on("dragend",function(a){var d=$(this).data("dragstart"),b=d.ele.x-d.click.x+a.originalEvent.pageX,
a=d.ele.y-d.click.y+a.originalEvent.pageY;$(this).css({opacity:1,top:a,left:b,right:"auto",bottom:"auto"})});a.parent().on("dragleave",function(){})},format:{time:function(a,c){var d=new Date(1E3*a),b=[];b.push(("0"+d.getHours()).slice(-2));b.push(("0"+d.getMinutes()).slice(-2));"short"!=c&&b.push(("0"+d.getSeconds()).slice(-2));return b.join(":")},date:function(a,c){var d=new Date(1E3*a),b="Sun Mon Tue Wed Thu Fri Sat".split(" "),e=[];"long"==c&&e.push(b[d.getDay()]);e.push(("0"+d.getDate()).slice(-2));
e.push("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ")[d.getMonth()]);"short"!=c&&e.push(d.getFullYear());return e.join(" ")},dateTime:function(a,c){return UI.format.date(a,c)+", "+UI.format.time(a,c)},duration:function(a){var c=[0.001,1E3,60,60,24,7,52,1E9],d="ms sec min hr day week year".split(" "),b={},e;for(e in d){var a=a/c[e],h=Math.round(a%c[Number(e)+1]);b[d[e]]=h;a-=h}var m;for(e=d.length-1;0<=e;e--)if(0<b[d[e]]){m=d[e];break}c=$("<span>");switch(m){case "year":c.append(UI.format.addUnit(b.year,
"years, ")).append(UI.format.addUnit(b.week,"wks"));break;case "week":c.append(UI.format.addUnit(b.week,"wks, ")).append(UI.format.addUnit(b.day,"days"));break;case "day":c.append(UI.format.addUnit(b.day,"days, ")).append(UI.format.addUnit(b.hr,"hrs"));break;default:c.append([("0"+b.hr).slice(-2),("0"+b.min).slice(-2),("0"+b.sec).slice(-2)+(b.ms?"."+b.ms:"")].join(":"))}return c[0].innerHTML},number:function(a){if(isNaN(Number(a))||0==a)return a;var c=Math.pow(10,3-Math.floor(Math.log(a)/Math.LN10)-
1),a=Math.round(a*c)/c;if(1E4<a){number=a.toString().split(".");for(a=/(\d+)(\d{3})/;a.test(number[0]);)number[0]=number[0].replace(a,"$1 $2");a=number.join(".")}return a},status:function(a){var c=$("<span>");if("undefined"==typeof a.online)return c.text("Unknown, checking.."),"undefined"!=typeof a.error&&c.text(a.error),c;switch(a.online){case -1:c.text("Enabling");break;case 0:c.text("Unavailable").addClass("red");break;case 1:c.text("Active").addClass("green");break;case 2:c.text("Standby").addClass("orange");
break;default:c.text(a.online)}"error"in a&&c.text(a.error);return c},capital:function(a){return a.charAt(0).toUpperCase()+a.substring(1)},addUnit:function(a,c){var d=$("<span>").html(a);d.append($("<span>").addClass("unit").html(c));return d[0].innerHTML},bytes:function(a,c){var d="bytes KiB MiB GiB TiB PiB".split(" ");if(0==a)unit=d[0];else{var b=Math.floor(Math.log(Math.abs(a))/Math.log(1024));0>b?unit=d[0]:(a/=Math.pow(1024,b),unit=d[b])}return UI.format.addUnit(UI.format.number(a),unit+(c?"/s":
""))}},navto:function(a,c){var d=location.hash,b=d.split("@");b[0]=[mist.user.name,mist.user.host].join("&");b[1]=[a,c].join("&");"undefined"!=typeof screenlog&&screenlog.navto(b[1]);location.hash=b.join("@");location.hash==d&&$(window).trigger("hashchange")},showTab:function(a,c){var d=UI.elements.main;if(mist.user.loggedin){if(!("ui_settings"in mist.data)){d.html("Loading..");mist.send(function(){UI.showTab(a,c)},{ui_settings:!0});return}mist.data.config.serverid&&(document.title=mist.data.config.serverid+
" - MistServer MI")}var b=UI.elements.menu.removeClass("hide").find('.plain:contains("'+a+'")').closest(".button");0<b.length&&(UI.elements.menu.find(".button.active").removeClass("active"),b.addClass("active"));if("undefined"!=typeof mistvideo)for(var e in mistvideo)if("embedded"in mistvideo[e])for(var h in mistvideo[e].embedded)try{mistvideo[e].embedded[h].player.unload(),delete mistvideo[e].embedded[h]}catch(m){}UI.interval.clear();d.html($("<h2>").text(a));switch(a){case "Login":if(mist.user.loggedin){UI.navto("Overview");
return}document.title="MistServer MI";UI.elements.menu.addClass("hide");UI.elements.connection.status.text("Disconnected").removeClass("green").addClass("red");d.append(UI.buildUI([{type:"help",help:"Please provide your account details.<br>You were asked to set these when MistController was started for the first time. If you did not yet set any account details, log in with your desired credentials to create a new account."},{label:"Host",help:"Url location of the MistServer API. Generally located at http://MistServerIP:4242/api",
"default":"http://localhost:4242/api",pointer:{main:mist.user,index:"host"}},{label:"Username",help:"Please enter your username here.",validate:["required"],pointer:{main:mist.user,index:"name"}},{label:"Password",type:"password",help:"Please enter your password here.",validate:["required"],pointer:{main:mist.user,index:"rawpassword"}},{type:"buttons",buttons:[{label:"Login",type:"save","function":function(){mist.user.password=MD5(mist.user.rawpassword);delete mist.user.rawpassword;mist.send(function(){UI.navto("Overview")})}}]}]));
break;case "Create a new account":UI.elements.menu.addClass("hide");d.append($("<p>").text("No account has been created yet in the MistServer at ").append($("<i>").text(mist.user.host)).append("."));d.append(UI.buildUI([{type:"buttons",buttons:[{label:"Select other host",type:"cancel",css:{"float":"left"},"function":function(){UI.navto("Login")}}]},{type:"custom",custom:$("<br>")},{label:"Desired username",type:"str",validate:["required"],help:"Enter your desired username. In the future, you will need this to access the Management Interface.",
pointer:{main:mist.user,index:"name"}},{label:"Desired password",type:"password",validate:["required",function(a,c){$(".match_password").not($(c)).trigger("change");return false}],help:"Enter your desired password. In the future, you will need this to access the Management Interface.",pointer:{main:mist.user,index:"rawpassword"},classes:["match_password"]},{label:"Repeat password",type:"password",validate:["required",function(a,c){return a!=$(".match_password").not($(c)).val()?{msg:'The fields "Desired password" and "Repeat password" do not match.',
classes:["red"]}:false}],help:"Repeat your desired password.",classes:["match_password"]},{type:"buttons",buttons:[{type:"save",label:"Create new account","function":function(){mist.send(function(){UI.navto("Account created")},{authorize:{new_username:mist.user.name,new_password:mist.user.rawpassword}});mist.user.password=MD5(mist.user.rawpassword);delete mist.user.rawpassword}}]}]));break;case "Account created":UI.elements.menu.addClass("hide");d.append($("<p>").text("Your account has been created succesfully.")).append(UI.buildUI([{type:"text",
text:"Would you like to enable all (currently) available protocols with their default settings?"},{type:"buttons",buttons:[{label:"Enable protocols",type:"save","function":function(){if(mist.data.config.protocols)d.append("Unable to enable all protocols as protocol settings already exist.<br>");else{d.append("Retrieving available protocols..<br>");mist.send(function(a){var c=[],b;for(b in a.capabilities.connectors)if(a.capabilities.connectors[b].required)d.append('Could not enable protocol "'+b+'" because it has required settings.<br>');
else{c.push({connector:b});d.append('Enabled protocol "'+b+'".<br>')}d.append("Saving protocol settings..<br>");mist.send(function(){d.append("Protocols enabled. Redirecting..");setTimeout(function(){UI.navto("Overview")},5E3)},{config:{protocols:c}})},{capabilities:true})}}},{label:"Skip",type:"cancel","function":function(){UI.navto("Overview")}}]}]));break;case "Overview":if("undefined"==typeof mist.data.bandwidth){mist.send(function(){UI.navto(a)},{bandwidth:!0});d.append("Loading..");return}var g=
$("<span>").text("Loading.."),q=$("<span>"),o=$("<span>").addClass("logs"),l=$("<span>"),u=$("<span>"),f=$("<span>").text("Unknown"),j=$("<span>"),i=$("<span>");e={serverid:mist.data.config.serverid,debug:mist.data.config.debug,accesslog:mist.data.config.accesslog,prometheus:mist.data.config.prometheus};var k={};"bandwidth"in mist.data&&(k=mist.data.bandwidth,null==k&&(k={}),k.limit||(k.limit=""));var I=$("<select>").html($("<option>").val(1).text("bytes/s")).append($("<option>").val(1024).text("KiB/s")).append($("<option>").val(1048576).text("MiB/s")).append($("<option>").val(1073741824).text("GiB/s")),
b=parseURL(mist.user.host),b=b.protocol+b.host+b.port;d.append(UI.buildUI([{type:"help",help:"You can find most basic information about your MistServer here.<br>You can also set the debug level and force a save to the config.json file that MistServer uses to save your settings. "},{type:"span",label:"Version",pointer:{main:mist.data.config,index:"version"}},{type:"span",label:"Version check",value:g,LTSonly:!0},{type:"span",label:"Server time",value:u},{type:"span",label:"Licensed to",value:"license"in
mist.data.config?mist.data.config.license.user:"",LTSonly:!0},{type:"span",label:"Active products",value:f,LTSonly:!0},{type:"span",label:"Configured streams",value:mist.data.streams?Object.keys(mist.data.streams).length:0},{type:"span",label:"Active streams",value:q},{type:"span",label:"Current connections",value:l},{type:"span",label:"Enabled protocols",value:j},{type:"span",label:"Disabled protocols",value:i},{type:"span",label:"Recent problems",value:o},$("<br>"),{type:"str",label:"Human readable name",
pointer:{main:e,index:"serverid"},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:e,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:"selectinput",label:"Access log",selectinput:[["","Do not track"],["LOG","Log to MistServer log"],
[{type:"str",label:"Path",LTSonly:!0},"Log to file"]],pointer:{main:e,index:"accesslog"},help:"Enable access logs.",LTSonly:!0},{type:"selectinput",label:"Prometheus stats output",selectinput:[["","Disabled"],[{type:"str",label:"Passphrase",LTSonly:!0},"Enabled"]],pointer:{main:e,index:"prometheus"},help:"Make stats available in Prometheus format. These can be accessed via "+b+"/PASSPHRASE or "+b+"/PASSPHRASE.json.",LTSonly:!0},{type:"selectinput",label:"Load balancer bandwidth limit",selectinput:[["",
"Default (1 gbps)"],[{label:"Custom",type:"int",min:0,unit:I},"Custom"]],pointer:{main:k,index:"limit"},help:"This setting only applies when MistServer is combined with a load balancer. This is the amount of traffic this server is willing to handle.",LTSonly:!0},{type:"inputlist",label:"Load balancer bandwidth exceptions",pointer:{main:k,index:"exceptions"},help:"This setting only applies when MistServer is combined with a load balancer. Data sent to the hosts and subnets listed here will not count towards reported bandwidth usage.<br>Examples:<ul><li>192.168.0.0/16</li><li>localhost</li><li>10.0.0.0/8</li><li>fe80::/16</li></ul>",
LTSonly:!0},{type:"checkbox",label:"Force configurations save",pointer:{main:e,index:"save"},help:"Tick the box in order to force an immediate save to the config.json MistServer uses to save your settings. Saving will otherwise happen upon closing MistServer. Don't forget to press save after ticking the box."},{type:"buttons",buttons:[{type:"save",label:"Save","function":function(){var a={config:e},c={};c.limit=k.limit?I.val()*k.limit:0;c.exceptions=k.exceptions;if(c.exceptions===null)c.exceptions=
[];a.bandwidth=c;if(e.save)a.save=e.save;delete e.save;mist.send(function(){UI.navto("Overview")},a)}}]}]));if(mist.data.LTS){var ia=function(a){function c(a){if(a.update){var b="";"progress"in a.update&&(b=" ("+a.update.progress+"%)");g.text("Updating.."+b);setTimeout(function(){mist.send(function(a){c(a)},{update:true})},5E3)}else UI.showTab("Overview")}if(!a.update||!("uptodate"in a.update)){g.text("Unknown, checking..");setTimeout(function(){mist.send(function(a){"update"in a&&ia(a)},{checkupdate:true})},
5E3)}else if(a.update.error)g.addClass("red").text(a.update.error);else if(a.update.uptodate)g.text("Your version is up to date.").addClass("green");else if(a.update.progress){g.addClass("orange").removeClass("red").text("Updating..");c(a)}else g.addClass("red").text("Version outdated!").append($("<button>").text("Update").css({"font-size":"1em","margin-left":"1em"}).click(function(){if(confirm("Are you sure you want to execute a rolling update?")){g.addClass("orange").removeClass("red").text("Rolling update command sent..");
mist.send(function(a){c(a)},{autoupdate:true})}}))};ia(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 J=$("<table>").css("text-indent","0");f.html(J);J.append($("<tr>").append($("<th>").append("Product")).append($("<th>").append("Updates until")).append($("<th>").append("Use until")).append($("<th>").append("Max. simul. instances")));for(h in mist.data.config.license.active_products)b=mist.data.config.license.active_products[h],
J.append($("<tr>").append($("<td>").append(b.name)).append($("<td>").append(b.updates_final?b.updates_final:"&infin;")).append($("<td>").append(b.use_final?b.use_final:"&infin;")).append($("<td>").append(b.amount?b.amount:"&infin;")))}else f.text("None. ");f.append($("<a>").text("More details").attr("href","https://shop.mistserver.org/myinvoices").attr("target","_blank"))}}else g.text("");h=function(){var a={totals:{fields:["clients"],start:-10},active_streams:true};if(!("cabailities"in mist.data))a.capabilities=
true;mist.send(function(){ja()},a)};var ja=function(){q.text("active_streams"in mist.data?mist.data.active_streams?mist.data.active_streams.length:0:"?");if("totals"in mist.data&&"all_streams"in mist.data.totals)var a=mist.data.totals.all_streams.all_protocols.clients,a=a.length?UI.format.number(a[a.length-1][1]):0;else a="Loading..";l.text(a);u.text(UI.format.dateTime(mist.data.config.time,"long"));o.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 c in mist.data.log){var b=mist.data.log[c];if(["FAIL","ERROR"].indexOf(b[1])>-1){a++;var d=$("<span>").addClass("content").addClass("red"),e=b[2].split("|");for(c in e)d.append($("<span>").text(e[c]));o.append($("<div>").append($("<span>").append(UI.format.time(b[0]))).append(d));if(a==5)break}}a==0&&o.html("None.");a=[];b=[];for(c in mist.data.config.protocols){d=mist.data.config.protocols[c];a.indexOf(d.connector)>-1||a.push(d.connector)}j.text(a.length?
a.join(", "):"None.");if("capabilities"in mist.data){for(c in mist.data.capabilities.connectors)a.indexOf(c)==-1&&b.push(c);i.text(b.length?b.join(", "):"None.")}else i.text("Loading..")};h();ja();UI.interval.set(h,3E4);break;case "Protocols":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a)},{capabilities:!0});d.append("Loading..");return}var y=$("<tbody>");d.append(UI.buildUI([{type:"help",help:"You can find an overview of all the protocols and their relevant information here. You can add, edit or delete protocols."}])).append($("<button>").text("Delete all protocols").click(function(){if(confirm("Are you sure you want to delete all currently configured protocols?")){mist.data.config.protocols=
[];mist.send(function(){UI.navto("Protocols")},{config:mist.data.config})}})).append($("<button>").text("Enable default protocols").click(function(){var a=Object.keys(mist.data.capabilities.connectors),c;for(c in mist.data.config.protocols){var b=a.indexOf(mist.data.config.protocols[c].connector);b>-1&&a.splice(b,1)}var d=[];for(c in a)(!("required"in mist.data.capabilities.connectors[a[c]])||Object.keys(mist.data.capabilities.connectors[a[c]].required).length==0)&&d.push(a[c]);b="Click OK to enable disabled protocols with their default settings:\n ";
b=d.length?b+d.join(", "):b+"None.";if(d.length!=a.length){a=a.filter(function(a){return d.indexOf(a)<0});b=b+("\n\nThe following protocols can only be set manually:\n "+a.join(", "))}if(confirm(b)&&d.length){for(c in d)mist.data.config.protocols.push({connector:d[c]});mist.send(function(){UI.navto("Protocols")},{config:mist.data.config})}})).append("<br>").append($("<button>").text("New protocol").click(function(){UI.navto("Edit Protocol")}).css("clear","both")).append($("<table>").html($("<thead>").html($("<tr>").html($("<th>").text("Protocol")).append($("<th>").text("Status")).append($("<th>").text("Settings")).append($("<th>")))).append(y));
var ka=function(){function a(c){var b=mist.data.capabilities.connectors[c.connector];if(!b)return"";var d=[],e=["required","optional"],g;for(g in e)for(var z in b[e[g]])c[z]&&c[z]!=""?d.push(z+": "+c[z]):b[e[g]][z]["default"]&&d.push(z+": "+b[e[g]][z]["default"]);return $("<span>").addClass("description").text(d.join(", "))}y.html("");for(var c in mist.data.config.protocols){var b=mist.data.config.protocols[c];y.append($("<tr>").data("index",c).append($("<td>").text(b.connector)).append($("<td>").html(UI.format.status(b))).append($("<td>").html(a(b))).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)}}))))}};ka();UI.interval.set(function(){mist.send(function(){ka()})},
1E4);break;case "Edit Protocol":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,c)},{capabilities:!0});d.append("Loading..");return}var D=!1;""!=c&&0<=c&&(D=!0);var M={};for(h in mist.data.config.protocols)M[mist.data.config.protocols[h].connector]=1;var la=function(a){var c=mist.data.capabilities.connectors[a],b=mist.convertBuildOptions(c,n);if(D)var d=$.extend({},n);b.push({type:"hidden",pointer:{main:n,index:"connector"},value:a});b.push({type:"buttons",buttons:[{type:"save",
label:"Save","function":function(){var a={};D?a.updateprotocol=[d,n]:a.addprotocol=n;mist.send(function(){UI.navto("Protocols")},a)}},{type:"cancel",label:"Cancel","function":function(){UI.navto("Protocols")}}]});if("deps"in c&&c.deps!=""){J=$("<span>").text("Dependencies:");$ul=$("<ul>");J.append($ul);if(typeof c.deps=="string")c.deps=c.deps.split(", ");for(var e in c.deps){a=$("<li>").text(c.deps[e]+" ");$ul.append(a);typeof M[c.deps[e]]!="undefined"||typeof M[c.deps[e]+".exe"]!="undefined"?a.append($("<span>").addClass("green").text("(Configured)")):
a.append($("<span>").addClass("red").text("(Not yet configured)"))}b.unshift({type:"text",text:J[0].innerHTML})}return UI.buildUI(b)},M={};for(h in mist.data.config.protocols)M[mist.data.config.protocols[h].connector]=1;if(D){var r=mist.data.config.protocols[c],n=r;d.find("h2").append(' "'+r.connector+'"');d.append(la(r.connector))}else{d.html($("<h2>").text("New Protocol"));var n={},s=[["",""]];for(h in mist.data.capabilities.connectors)s.push([h,h]);var K=$("<span>");d.append(UI.buildUI([{label:"Protocol",
type:"select",select:s,"function":function(){$(this).getval()!=""&&K.html(la($(this).getval()))}}])).append(K)}break;case "Streams":if(!("capabilities"in mist.data)){d.html("Loading..");mist.send(function(){UI.navto(a)},{capabilities:!0});return}h=$("<button>");var E=$("<span>").text("Loading..");d.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(h).append($("<button>").text("Create a new stream").click(function(){UI.navto("Edit")}))])).append(E);""==c&&(e=mist.stored.get(),"viewmode"in e&&(c=e.viewmode));h.text("Switch to "+("thumbnails"==c?"list":"thumbnail")+" view").click(function(){mist.stored.set("viewmode",c=="thumbnails"?"list":"thumbnails");UI.navto("Streams",c=="thumbnails"?"list":"thumbnails")});var x=$.extend(!0,{},mist.data.streams),W=function(a,c){var b=$.extend({},c);delete b.meta;delete b.error;
b.online=2;b.name=a;b.ischild=true;return b},Y=function(c,b,e){E.remove();switch(c){case "thumbnails":var g=$("<div>").addClass("preview_icons"),f;f=e||[];b.sort();b.unshift("");E.remove();d.append($("<h2>").text(a)).append(UI.buildUI([{label:"Filter the streams",type:"datalist",datalist:b,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()})}}]));b.shift();d.append($("<span>").addClass("description").text("Choose a stream below.")).append(g);for(var h in b){var c=b[h],j="",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 c={};c.deletestream=[a];mist.send(function(){UI.navto("Streams")},c)}}),m=$("<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"))}),i=$("<button>").text("Embed").click(function(){UI.navto("Embed",$(this).closest("div").attr("data-stream"))}),X=$("<span>").addClass("image");if(c.indexOf("+")>-1){j=c.split("+");j=mist.data.streams[j[0]].source+j[1];m=k="";X.addClass("wildcard")}else{j=mist.data.streams[c].source;if(f.indexOf(c)>-1){i=e="";X.addClass("folder")}}g.append($("<div>").append($("<span>").addClass("streamname").text(c)).append(X).append($("<span>").addClass("description").text(j)).append($("<span>").addClass("button_container").append(m).append(k).append(e).append(i)).attr("title",
c).attr("data-stream",c))}break;default:var l=$("<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(l);
d.append(h);h.stupidtable();var n=function(){var a=[],c;for(c in mist.data.active_streams)a.push({streams:[mist.data.active_streams[c]],fields:["clients"],start:-2});mist.send(function(){$.extend(true,x,mist.data.streams);var a=0;l.html("");b.sort();for(var c in b){var d=b[c],e;e=d in mist.data.streams?mist.data.streams[d]:x[d];var g=$("<td>").css("text-align","right").html($("<span>").addClass("description").text("Loading..")),f=0;if(typeof mist.data.totals!="undefined"&&typeof mist.data.totals[d]!=
"undefined"){var h=mist.data.totals[d].all_protocols.clients,f=0;if(h.length){for(a in h)f=f+h[a][1];f=Math.round(f/h.length)}}g.html(UI.format.number(f));if(f==0&&e.online==1)e.online=2;f=$("<td>").css("text-align","right").css("white-space","nowrap");(!("ischild"in e)||!e.ischild)&&f.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 c={};mist.data.LTS?c.deletestream=[a]:c.streams=mist.data.streams;mist.send(function(){UI.navto("Streams")},c)}}));h=$("<span>").text(d);e.ischild&&h.css("padding-left","1em");var z=UI.format.status(e),j=$("<button>").text("Preview").click(function(){UI.navto("Preview",$(this).closest("tr").data("index"))}),k=$("<button>").text("Embed").click(function(){UI.navto("Embed",$(this).closest("tr").data("index"))});if("filesfound"in x[d]){z.html("");j="";g.html("");
k=""}l.append($("<tr>").data("index",d).html($("<td>").html(h).attr("title",d).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(z)).append(g).append($("<td>").css("white-space","nowrap").html(j).append(k)).append(f));a++}},{totals:a,active_streams:true})};if(mist.data.LTS){var o=0,p=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)){x[f].source=x[f].source+"*";x[f].filesfound=null;mist.send(function(a,c){var b=c.stream,d;for(d in a.browse.files)for(var e in mist.data.capabilities.inputs)if(!(e.indexOf("Buffer")>=0||e.indexOf("Buffer.exe")>=0||e.indexOf("Folder")>=0||e.indexOf("Folder.exe")>=0)&&mist.inputMatch(mist.data.capabilities.inputs[e].source_match,"/"+a.browse.files[d])){var g=b+"+"+a.browse.files[d];
x[g]=W(g,mist.data.streams[b]);x[g].source=mist.data.streams[b].source+a.browse.files[d]}"files"in a.browse&&a.browse.files.length?x[b].filesfound=true:mist.data.streams[b].filesfound=false;p++;if(o==p){mist.send(function(){n()},{active_streams:true});UI.interval.set(function(){n()},5E3)}},{browse:mist.data.streams[f].source},{stream:f});o++}}if(o==0){mist.send(function(){n()},{active_streams:true});UI.interval.set(function(){n()},5E3)}}else{mist.send(function(){n()},{active_streams:true});UI.interval.set(function(){n()},
5E3)}}};if(mist.data.LTS){var Z=0,ma=0,s={},na=[];for(e in mist.data.streams)if(mist.inputMatch((mist.data.capabilities.inputs.Folder||mist.data.capabilities.inputs["Folder.exe"]).source_match,mist.data.streams[e].source))na.push(e),mist.send(function(a,b){var d=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||mist.inputMatch(mist.data.capabilities.inputs[g].source_match,"/"+a.browse.files[e])&&(s[d+"+"+a.browse.files[e]]=
true);ma++;Z==ma&&mist.send(function(){for(var a in mist.data.active_streams){var b=mist.data.active_streams[a].split("+");if(b.length>1&&b[0]in mist.data.streams){s[mist.data.active_streams[a]]=true;x[mist.data.active_streams[a]]=W(mist.data.active_streams[a],mist.data.streams[b[0]])}}s=Object.keys(s);s=s.concat(Object.keys(mist.data.streams));s.sort();Y(c,s,na)},{active_streams:true})},{browse:mist.data.streams[e].source},{stream:e}),Z++;0==Z&&mist.send(function(){for(var a in mist.data.active_streams){var b=
mist.data.active_streams[a].split("+");if(b.length>1&&b[0]in mist.data.streams){s[mist.data.active_streams[a]]=true;x[mist.data.active_streams[a]]=W(mist.data.active_streams[a],mist.data.streams[b[0]])}}s=Object.keys(s);mist.data.streams&&(s=s.concat(Object.keys(mist.data.streams)));s.sort();Y(c,s)},{active_streams:!0})}else Y(c,Object.keys(mist.data.streams));break;case "Edit":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a,c)},{capabilities:!0});d.append("Loading..");
return}D=!1;""!=c&&(D=!0);D?(f=c,n=mist.data.streams[f],d.find("h2").append(' "'+f+'"')):(d.html($("<h2>").text("New Stream")),n={});f=[];for(h in mist.data.capabilities.inputs)f.push(mist.data.capabilities.inputs[h].source_match);var R=$("<div>"),oa=function(a){var b={};if(!mist.data.streams)mist.data.streams={};mist.data.streams[n.name]=n;c!=n.name&&delete mist.data.streams[c];b.addstream={};b.addstream[n.name]=n;if(c!=n.name)b.deletestream=[c];if(n.stop_sessions&&c!=""){b.stop_sessions=c;delete n.stop_sessions}mist.send(function(){delete mist.data.streams[n.name].online;
delete mist.data.streams[n.name].error;UI.navto(a,a=="Preview"?n.name:"")},b)},pa=$("<style>").text("button.saveandpreview { display: none; }"),F=$("<span>"),aa=function(){var a=d.find("[name=name]").val();if(a){var c=parseURL(mist.user.host),b=d.find("[name=source]").val(),e=b.match(/@.*/);e&&(e=e[0].substring(1));var g=b.replace(/(?:.+?):\/\//,""),g=g.split("/"),g=g[0],g=g.split(":"),g=g[0];(b=b.match(/:\d+/))&&(b=b[0]);var f={},h=["RTMP","RTSP","RTMP.exe","RTSP.exe"],j;for(j in h)h[j]in mist.data.capabilities.connectors&&
(f[h[j]]=mist.data.capabilities.connectors[h[j]].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(j in mist.data.config.protocols){var m=mist.data.config.protocols[j];if(m.connector==k){if("port"in m)f[k]=m.port;break}}f[k]=f[k]==h[k]?"":":"+f[k]}f.TS="";f["TS.exe"]="";F.find(".field").closest("label").hide();for(j in f){var i;k=b?b:f[j];switch(j){case "RTMP":case "RTMP.exe":i="rtmp://"+c.host+k+"/"+(e?e:"live")+"/";F.find(".field.RTMPurl").setval(i).closest("label").show();
F.find(".field.RTMPkey").setval(a==""?"STREAMNAME":a).closest("label").show();i=i+(a==""?"STREAMNAME":a);break;case "RTSP":case "RTSP.exe":i="rtsp://"+c.host+k+"/"+(a==""?"STREAMNAME":a)+(e?"?pass="+e:"");break;case "TS":case "TS.exe":i="udp://"+(g==""?c.host:g)+k+"/"}F.find(".field."+j.replace(".exe","")).setval(i).closest("label").show()}}};d.append(UI.buildUI([{label:"Stream name",type:"str",validate:["required","streamname"],pointer:{main:n,index:"name"},help:"Set the name this stream will be recognised by for players and/or stream pushing."},
{label:"Source",type:"browse",filetypes:f,pointer:{main:n,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:&nbsp;/PATH/FILE<br> Windows:&nbsp;/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:&nbsp;/PATH/<br> Windows:&nbsp;/cygdrive/DRIVE/PATH/ </td> <td class=LTSonly> A folder stream makes all the recognised files in the selected folder available as a stream. </td> </tr> <tr><td colspan=3>&nbsp;</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 class=LTSonly>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 class=LTSonly> 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>&nbsp;</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 class=LTSonly>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 class=LTSonly> 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>".replace(/LTSonly/g,
mist.data.LTS?'""':"LTSonly"),"function":function(){var a=$(this).val();pa.remove();F.html("");if(a!=""){var c=null,b;for(b in mist.data.capabilities.inputs)if(typeof mist.data.capabilities.inputs[b].source_match!="undefined"&&mist.inputMatch(mist.data.capabilities.inputs[b].source_match,a)){c=b;break}if(c===null)R.html($("<h3>").text("Unrecognized input").addClass("red")).append($("<span>").text("Please edit the stream source.").addClass("red"));else{c=mist.data.capabilities.inputs[c];R.html($("<h3>").text(c.name+
" Input options"));var e=mist.convertBuildOptions(c,n);"always_match"in mist.data.capabilities.inputs[b]&&mist.inputMatch(mist.data.capabilities.inputs[b].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:n,index:"always_on"}});R.append(UI.buildUI(e));if(c.name=="Folder")d.append(pa);else if(["Buffer","Buffer.exe","TS","TS.exe"].indexOf(c.name)>-1){b=[$("<br>"),$("<span>").text("Configure your source to push to:")];
switch(c.name){case "Buffer":case "Buffer.exe":b.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"});b.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"});b.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"});
b.push({label:"RTSP",type:"span",clipboard:true,readonly:true,classes:["RTSP"]});break;case "TS":case "TS.exe":a.charAt(0)=="/"?b=[]:b.push({label:"TS",type:"span",clipboard:true,readonly:true,classes:["TS"]})}F.html(UI.buildUI(b));aa()}}}}},{label:"Stop sessions",type:"checkbox",help:"When saving these stream settings, kill this stream's current connections.",LTSonly:!0,pointer:{main:n,index:"stop_sessions"}},F,$("<br>"),{type:"custom",custom:R},$("<br>"),$("<h3>").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.<br>Unsure how you should fill in your encryption or missing your preferred encryption? Please contact us."},{label:"License acquisition url",type:"str",LTSonly:!0,pointer:{main:n,index:"la_url"}},$("<br>"),{label:"Content key",type:"str",LTSonly:!0,pointer:{main:n,index:"contentkey"}},{type:"text",text:" - or - "},{label:"Key ID",type:"str",LTSonly:!0,pointer:{main:n,
index:"keyid"}},{label:"Key seed",type:"str",LTSonly:!0,pointer:{main:n,index:"keyseed"}},{type:"buttons",buttons:[{type:"cancel",label:"Cancel","function":function(){UI.navto("Streams")}},{type:"save",label:"Save","function":function(){oa("Streams")}},{type:"save",label:"Save and Preview","function":function(){oa("Preview")},classes:["saveandpreview"]}]}]));d.find("[name=name]").keyup(function(){aa()});aa();break;case "Preview":""==c&&UI.navto("Streams");b=":8080";for(h in mist.data.config.protocols)if(r=
mist.data.config.protocols[h],"HTTP"==r.connector||"HTTP.exe"==r.connector)b=r.port?":"+r.port:":8080";var f=parseURL(mist.user.host),N=f.protocol+f.host+b+"/",K=$("<div>").css({display:"flex","flex-flow":"row wrap"}),f="";-1==c.indexOf("+")&&(f=$("<button>").text("Settings").addClass("settings").click(function(){UI.navto("Edit",c)}));d.html($("<div>").addClass("bigbuttons").append(f).append($("<button>").text("Embed").addClass("embed").click(function(){UI.navto("Embed",c)})).append($("<button>").addClass("cancel").addClass("return").text("Return").click(function(){UI.navto("Streams")}))).append($("<h2>").text('Preview of "'+
c+'"')).append(K);var G=encodeURIComponent(c);h=$("<div>");K.append(h);var ba=$("<div>"),S=$("<select>").append($("<option>").text("Automatic").val("")).change(function(){ca()}),O=$("<select>").append($("<option>").text("Automatic").val("")).change(function(){ca()}),f=UI.buildUI([{label:"Use player",type:"DOMfield",DOMfield:S,help:"Choose a player to preview"},{label:"Use source",type:"DOMfield",DOMfield:O,help:"Choose an output type to preview"}]),P=$("<div>").addClass("mistvideo").text("Loading player..");
h.append(P).append(ba).append(f);var ca=function(){A.html("");if(typeof mistvideo!="undefined")for(var a in mistvideo)if("embedded"in mistvideo[a])for(var b in mistvideo[a].embedded)try{mistvideo[a].embedded[b].player.unload()}catch(d){}a={target:P[0],maxheight:window.innerHeight-$("header").height(),maxwidth:window.innerWidth-UI.elements.menu.width()-100,host:N.replace(/\/$/,""),loop:true};if(S.val()!="")a.forcePlayer=S.val();if(O.val()!="")a.forceSource=O.val();mistPlay(c,a)},A=$("<div>").addClass("player_log");
h.append($("<div>").append($("<h3>").text("Player log:")).append(A));var qa="";P.on("log error",function(a){var c=false;A.height()+A.scrollTop()==A[0].scrollHeight&&(c=true);var b=a.type+a.originalEvent.message,d="["+UI.format.time((new Date).getTime()/1E3)+"]";if(qa==b){var a=A.children().last(),c=a.children("[data-amount]"),e=c.attr("data-amount");e++;c.text("("+e+"x)").attr("data-amount",e);a.children(".timestamp").text(d)}else{A.append($("<div>").append($("<span>").addClass("timestamp").text(d).css("margin-right",
"0.5em")).append($("<span>").text(a.originalEvent.message)).append($("<span>").attr("data-amount",1).css("margin-left","0.5em")).addClass(a.type=="error"?"red":""));c&&A.scrollTop(A[0].scrollHeight)}qa=b});var ra=function(){ba.text("");var a=document.createElement("script");d.append(a);a.src=N+"player.js";a.onerror=function(){P.html("Failed to load player.js").append($("<button>").text("Reload").css("display","block").click(function(){ra()}))};a.onload=function(){for(var b in mistplayers)S.append($("<option>").text(mistplayers[b].name).val(b));
ca();P.on("initialized",function(){if(O.children().length<=1)for(var a in mistvideo[c].source){var b=mistvideo[c].source[a],d=UI.humanMime(b.type);O.append($("<option>").val(a).text(d?d+" @ "+b.url.substring(b.url.length-b.relurl.length,0):UI.format.capital(b.type)+" @ "+b.url.substring(b.url.length-b.relurl.length,0)))}a=mistvideo[c].embedded[mistvideo[c].embedded.length-1];d=UI.humanMime(a.player.options.source.type);ba.html("You're watching "+(d?d+" <span class=description>("+a.player.options.source.type+
")</span>":UI.format.capital(a.player.options.source.type))+" through "+mistplayers[a.selectedPlayer].name+".")});d[0].removeChild(a)}};ra();h=$("<div>").append($("<h3>").text("Meta information"));var T=$("<span>").text("Loading..");h.append(T);K.append(h);$.ajax({type:"GET",url:N+"json_"+G+".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)T.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"],
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,j;for(j in e){var k=e[j],i=c.tracks[k];switch(i.type){case "audio":d.audio.content.push({header:"Track "+k.split("_").pop(),body:[i.codec,UI.format.duration((i.lastms-i.firstms)/1E3)+"<br><span class=description>"+UI.format.duration(i.firstms/1E3)+
" to "+UI.format.duration(i.lastms/1E3)+"</span>",b(i,"bps"),b(i,"maxbps"),i.channels,UI.format.addUnit(UI.format.number(i.rate),"Hz"),"language"in i?i.language:"unknown",g]});g++;break;case "video":d.video.content.push({header:"Track "+k.split("_").pop(),body:[i.codec,UI.format.duration((i.lastms-i.firstms)/1E3)+"<br><span class=description>"+UI.format.duration(i.firstms/1E3)+" to "+UI.format.duration(i.lastms/1E3)+"</span>",b(i,"bps"),b(i,"maxbps"),UI.format.addUnit(i.width,"x ")+UI.format.addUnit(i.height,
"px"),UI.format.addUnit(UI.format.number(i.fpks/1E3),"fps"),"language"in i?i.language:"unknown",f]});f++;break;case "meta":case "subtitle":if(i.codec=="subtitle"||i.type=="subtitle"){d.subtitle.content.push({header:"Track "+k.split("_").pop(),body:[i.codec,UI.format.duration((i.lastms-i.firstms)/1E3)+"<br><span class=description>"+UI.format.duration(i.firstms/1E3)+" to "+UI.format.duration(i.lastms/1E3)+"</span>",b(i,"bps"),b(i,"maxbps"),"language"in i?i.language:"unknown",h]});h++}}}b=["audio","video",
"subtitle"];j=$("<div>").css({display:"flex","flex-flow":"row wrap","font-size":"0.9em"});for(k in b)d[b[k]].content.length&&j.append(UI.buildVheaderTable(d[b[k]]).css("width","auto"));a.push($("<span>").text("Tracks:"));a.push(j);T.html(UI.buildUI(a))}},error:function(){T.html("Error while retrieving stream info.")}});break;case "Embed":""==c&&UI.navTo("Streams");f="";-1==c.indexOf("+")&&(f=$("<button>").addClass("settings").text("Settings").click(function(){UI.navto("Edit",c)}));d.html($("<div>").addClass("bigbuttons").append(f).append($("<button>").text("Preview").addClass("preview").click(function(){UI.navto("Preview",
c)})).append($("<button>").addClass("cancel").addClass("return").text("Return").click(function(){UI.navto("Streams")}))).append($("<h2>").text('Embed "'+c+'"'));var U=$("<span>");d.append(U);G=encodeURIComponent(c);f=parseURL(mist.user.host);b={"":{port:":8080"}};for(h in mist.data.config.protocols){r=mist.data.config.protocols[h];if("HTTP"==r.connector||"HTTP.exe"==r.connector)b[""].port=r.port?":"+r.port:":8080";if("HTTPS"==r.connector||"HTTPS.exe"==r.connector)b.s={},b.s.port=r.port?":"+r.port:
":4433"}var H=N="http://"+f.host+b[""].port+"/";if(otherhost.host||otherhost.https)H=(otherhost.https&&"s"in b?"https://":"http://")+(otherhost.host?otherhost.host:f.host)+(otherhost.https&&"s"in b?b.s.port:b[""].port)+"/";var da={forcePlayer:"",forceType:"",controls:!0,autoplay:!0,loop:!1,width:"",height:"",maxwidth:"",maxheight:"",poster:"",urlappend:"",setTracks:{}},p=$.extend({},da);h=UI.stored.getOpts();"embedoptions"in h&&(p=$.extend(p,h.embedoptions,!0),"object"!=typeof p.setTracks&&(p.setTracks=
{}));h={};switch(p.controls){case "stock":h.controls="stock";break;case !0:h.controls=1;break;case !1:h.controls=0}var v=function(){function a(b){switch(typeof b){case "string":return $.isNumeric(b)?b:'"'+b+'"';case "object":return JSON.stringify(b);default:return b}}UI.stored.saveOpt("embedoptions",p);for(var b=c+"_",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 b=b+e,d=['target: document.getElementById("'+b+'")'],
f;for(f in p)p[f]!=da[f]&&(typeof p[f]!="object"||JSON.stringify(p[f])!=JSON.stringify(da[f]))&&d.push(f+": "+a(p[f]));f=[];f.push('<div class="mistvideo" id="'+b+'">');f.push(" <noscript>");f.push(' <a href="'+H+G+'.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("'+c+'",{');f.push(" "+d.join(",\n "));f.push(" });");f.push(" };");
f.push(" if (!window.mistplayers) {");f.push(' var p = document.createElement("script");');f.push(' p.src = "'+H+'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")},ea=$("<span>").text("Loading.."),r=v(p),L=$("<div>").text("Loading..").css("display","flex"),sa="";"s"in b&&(sa=UI.buildUI([{label:"Use HTTPS",type:"checkbox","function":function(){if($(this).getval()!=
otherhost.https){otherhost.https=$(this).getval();UI.navto("Embed",c)}},value:otherhost.https}]).find("label"));U.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","text").addClass("field").val(otherhost.host?otherhost.host:f.host)).append($("<span>").addClass("unit").append($("<button>").text("Apply").click(function(){otherhost.host=
$(this).closest("label").find("input").val();UI.navto("Embed",c)}))))).append(sa)).append(UI.buildUI([$("<h3>").text("Urls"),{label:"Stream info json",type:"str",value:H+"json_"+G+".js",readonly:!0,clipboard:!0,help:"Information about this stream as a json page."},{label:"Stream info script",type:"str",value:H+"info_"+G+".js",readonly:!0,clipboard:!0,help:"This script loads information about this stream into a mistvideo javascript object."},{label:"HTML page",type:"str",value:H+G+".html",readonly:!0,
qrcode:!0,clipboard:!0,help:"A basic html containing the embedded stream."},$("<h3>").text("Embed code"),{label:"Embed code",type:"textarea",value:r,rows:r.split("\n").length+3,readonly:!0,classes:["embed_code"],clipboard:!0,help:"Include this code on your webpage to embed the stream. The options below can be used to configure how your content is displayed."},$("<h4>").text("Embed code options (optional)").css("margin-top",0),{type:"help",help:"Use these controls to customise what this embedded video will look like.<br>Not all players have all of these options."},
{label:"Force player",type:"select",select:[["","Automatic"]],pointer:{main:p,index:"forcePlayer"},classes:["forcePlayer"],"function":function(){p.forcePlayer=$(this).getval();$(".embed_code").setval(v(p))},help:"Only use this particular player."},{label:"Force source",type:"select",select:[["","Automatic"]],pointer:{main:p,index:"forceType"},classes:["forceType"],"function":function(){p.forceType=$(this).getval();$(".embed_code").setval(v(p))},help:"Only use this particular source."},{label:"Controls",
type:"select",select:[["1","MistServer Controls"],["stock","Player controls"],["0","None"]],pointer:{main:h,index:"controls"},"function":function(){p.controls=$(this).getval()==1;switch($(this).getval()){case 0:p.controls=false;break;case 1:p.controls=true;break;case "stock":p.controls="stock"}$(".embed_code").setval(v(p))},help:"The type of controls that should be shown."},{label:"Autoplay",type:"checkbox",pointer:{main:p,index:"autoplay"},"function":function(){p.autoplay=$(this).getval();$(".embed_code").setval(v(p))},
help:"Whether or not the video should play as the page is loaded."},{label:"Loop",type:"checkbox",pointer:{main:p,index:"loop"},"function":function(){p.loop=$(this).getval();$(".embed_code").setval(v(p))},help:"If the video should restart when the end is reached."},{label:"Force width",type:"int",min:0,unit:"px",pointer:{main:p,index:"width"},"function":function(){p.width=$(this).getval();$(".embed_code").setval(v(p))},help:"Enforce a fixed width."},{label:"Force height",type:"int",min:0,unit:"px",
pointer:{main:p,index:"height"},"function":function(){p.height=$(this).getval();$(".embed_code").setval(v(p))},help:"Enforce a fixed height."},{label:"Maximum width",type:"int",min:0,unit:"px",pointer:{main:p,index:"maxwidth"},"function":function(){p.maxwidth=$(this).getval();$(".embed_code").setval(v(p))},help:"The maximum width this video can use."},{label:"Maximum height",type:"int",min:0,unit:"px",pointer:{main:p,index:"maxheight"},"function":function(){p.maxheight=$(this).getval();$(".embed_code").setval(v(p))},
help:"The maximum height this video can use."},{label:"Poster",type:"str",pointer:{main:p,index:"poster"},"function":function(){p.poster=$(this).getval();$(".embed_code").setval(v(p))},help:"URL to an image that is displayed when the video is not playing."},{label:"Video URL addition",type:"str",pointer:{main:p,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(){p.urlappend=
$(this).getval();$(".embed_code").setval(v(p))}},{label:"Preselect tracks",type:"DOMfield",DOMfield:L,help:"Pre-select these tracks."},$("<h3>").text("Protocol stream urls"),ea]));$.ajax({type:"GET",url:H+"json_"+G+".js",success:function(a){var b=[],c=U.find(".forceType"),d;for(d in a.source){var e=a.source[d],f=UI.humanMime(e.type);b.push({label:f?f+" <span class=description>("+e.type+")</span>":UI.format.capital(e.type),type:"str",value:e.url,readonly:true,qrcode:true,clipboard:true});f=UI.humanMime(e.type);
c.append($("<option>").text(f?f+" ("+e.type+")":UI.format.capital(e.type)).val(e.type))}ea.html(UI.buildUI(b));L.html("");b={};for(d in a.meta.tracks){c=a.meta.tracks[d];c.type!="audio"&&c.type!="video"||(c.type in b?b[c.type].push([c.trackid,UI.format.capital(c.type)+" track "+(b[c.type].length+1)]):b[c.type]=[["",UI.format.capital(c.type)+" track 1"]])}if(Object.keys(b).length){L.closest("label").show();for(d in b){a=$("<select>").attr("data-type",d).css("flex-grow","1").change(function(){$(this).val()==
""?delete p.setTracks[$(this).attr("data-type")]:p.setTracks[$(this).attr("data-type")]=$(this).val();$(".embed_code").setval(v(p))});L.append(a);b[d].push([-1,"No "+d]);for(var g in b[d])a.append($("<option>").val(b[d][g][0]).text(b[d][g][1]));if(d in p.setTracks){a.val(p.setTracks[d]);if(a.val()==null){a.val("");delete p.setTracks[d];$(".embed_code").setval(v(p))}}}}else L.closest("label").hide()},error:function(){ea.html("Error while retrieving stream info.");L.closest("label").hide();p.setTracks=
{}}});h=document.createElement("script");h.src=N+"player.js";document.head.appendChild(h);h.onload=function(){var a=U.find(".forcePlayer"),b;for(b in mistplayers)a.append($("<option>").text(mistplayers[b].name).val(b));document.head.removeChild(this)};h.onerror=function(){document.head.removeChild(this)};break;case "Push":var C=$("<div>").text("Loading..");d.append(C);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>");if(g=="Automatic"&&f.length>=4){h.append($("<span>").text(f[2]));f[3]&&h.append($("<span>").text(", schedule on "+(new Date(f[3]*1E3)).toLocaleString()));f.length>=5&&f[4]&&h.append($("<span>").text(", complete on "+(new Date(f[4]*1E3)).toLocaleString()))}else f.length>=4&&f[2]!=f[3]?
h.append($("<span>").text(f[2])).append($("<span>").html("&#187").addClass("unit").css("margin","0 0.5em")).append($("<span>").text(f[3])):h.append($("<span>").text(f[2]));var i=$("<td>").append($("<button>").text(g=="Automatic"?"Remove":"Stop").click(function(){if(confirm("Are you sure you want to "+$(this).text().toLowerCase()+" this push?\n"+f[1]+" to "+f[2])){var a=$(this).closest("tr");a.html($("<td colspan=99>").html($("<span>").addClass("red").text(g=="Automatic"?"Removing..":"Stopping..")));
if(g=="Automatic"){var c=f.slice(1);mist.send(function(){a.remove()},{push_auto_remove:[c]})}else mist.send(function(){b([f[0]])},{push_stop:[f[0]]})}}));if(g=="Automatic"){i.prepend($("<button>").text("Edit").click(function(){UI.navto("Start Push","auto_"+($(this).closest("tr").index()-1))}));i.append($("<button>").text("Stop pushes").click(function(){if(confirm('Are you sure you want to stop all pushes matching \n"'+f[1]+" to "+f[2]+'"?'+(d.wait!=0?"\n\nRetrying is enabled. You'll probably want to set that to 0.":
""))){var c=$(this);c.text("Stopping pushes..");var g=[],h;for(h in a.push_list)if(f[1]==a.push_list[h][1]&&f[2]==a.push_list[h][2]){g.push(a.push_list[h][0]);e.find("tr[data-pushid="+a.push_list[h][0]+"]").html($("<td colspan=99>").html($("<span>").addClass("red").text("Stopping..")))}mist.send(function(){c.text("Stop pushes");b(g)},{push_stop:g,push_settings:{wait:0}})}}))}return $("<tr>").attr("data-pushid",f[0]).append($("<td>").text(f[1])).append($("<td>").append(h.children())).append(i)}C.html(UI.buildUI([{type:"help",
help:"You can push streams to files or other servers, allowing them to broadcast your stream as well."}]));var d=a.push_settings;d||(d={});var e=$("<table>").append($("<tr>").append($("<th>").text("Stream")).append($("<th>").text("Target")).append($("<th>"))),f=e.clone();if("push_list"in a)for(var g in a.push_list)e.append(c(a.push_list[g],"Manual"));if("push_auto_list"in a)for(g in a.push_auto_list){var h=a.push_auto_list[g].slice();h.unshift(-1);f.append(c(h,"Automatic"))}C.append($("<h3>").text("Automatic pushes")).append(UI.buildUI([{label:"Delay before retry",
unit:"s",type:"int",min:0,help:"How long the delay should be before MistServer retries an automatic push.<br>If set to 0, it does not retry.","default":0,pointer:{main:d,index:"wait"},LTSonly:1},{label:"Maximum retries",unit:"/s",type:"int",min:0,help:"The maximum amount of retries per second (for all automatic pushes).<br>If set to 0, there is no limit.","default":0,pointer:{main:d,index:"maxspeed"},LTSonly:1},{type:"buttons",buttons:[{type:"save",label:"Save","function":function(){mist.send(function(){UI.navto("Push")},
{push_settings:d})}}]}])).append($("<button>").text("Add an automatic push").click(function(){UI.navto("Start Push","auto")}));f.find("tr").length==1?C.append($("<div>").text("No automatic pushes have been configured.").addClass("text").css("margin-top","0.5em")):C.append(f);C.append($("<h3>").text("Pushes")).append($("<button>").text("Start a push").click(function(){UI.navto("Start Push")}));if(e.find("tr").length==1)C.append($("<div>").text("No pushes are active.").addClass("text").css("margin-top",
"0.5em"));else{var f=[],h=[],i=$("<select>").css("margin-left","0.5em").append($("<option>").text("Any stream").val("")),j=$("<select>").css("margin-left","0.5em").append($("<option>").text("Any target").val(""));for(g in a.push_list){f.indexOf(a.push_list[g][1])==-1&&f.push(a.push_list[g][1]);h.indexOf(a.push_list[g][2])==-1&&h.push(a.push_list[g][2])}f.sort();h.sort();for(g in f)i.append($("<option>").text(f[g]));for(g in h)j.append($("<option>").text(h[g]));C.append($("<button>").text("Stop all pushes").click(function(){var c=
[],d;for(d in a.push_list)c.push(a.push_list[d][0]);if(c.length!=0&&confirm("Are you sure you want to stop all pushes?")){mist.send(function(){b(c)},{push_stop:c});e.find("tr:not(:first-child)").html($("<td colspan=99>").append($("<span>").addClass("red").text("Stopping..")));$(this).remove()}})).append($("<label>").css("margin-left","1em").append($("<span>").text("Stop all pushes that match: ").css("font-size","0.9em")).append(i).append($("<span>").css("margin-left","0.5em").text("and").css("font-size",
"0.9em")).append(j).append($("<button>").css("margin-left","0.5em").text("Apply").click(function(){var c=i.val(),d=j.val();if(c==""&&d=="")return alert("Looks like you want to stop all pushes. Maybe you should use that button?");var f={},g;for(g in a.push_list)if((c==""||a.push_list[g][1]==c)&&(d==""||a.push_list[g][2]==d))f[a.push_list[g][0]]=a.push_list[g];if(Object.keys(f).length==0)return alert("No matching pushes.");c="Are you sure you want to stop these pushes?\n\n";for(g in f)c=c+(f[g][1]+
" to "+f[g][2]+"\n");if(confirm(c)){f=Object.keys(f);mist.send(function(){b(f)},{push_stop:f});for(g in f)e.find("tr[data-pushid="+f[g]+"]").html($("<td colspan=99>").html($("<span>").addClass("red").text("Stopping..")))}}))).append(e)}},{push_settings:1,push_list:1,push_auto_list:1});break;case "Start Push":if(!("capabilities"in mist.data)){d.append("Loading Mist capabilities..");mist.send(function(){UI.navto("Start Push",c)},{capabilities:1});return}var t,V=function(a){var b=false,f=c.split("_");
c=f[0];f.length==2&&(b=f[1]);if(b!==false&&typeof a=="undefined")mist.send(function(a){V(a.push_auto_list[b])},{push_auto_list:1});else{var e=[],g;for(g in mist.data.capabilities.connectors){f=mist.data.capabilities.connectors[g];"push_urls"in f&&(e=e.concat(f.push_urls))}c=="auto"&&d.find("h2").text("Add automatic push");var h={};if(c=="auto"&&typeof a!="undefined"){h={stream:a[0],target:a[1]};if(a.length>=3)h.scheduletime=a[2];if(a.length>=4)h.completetime=a[3];if(h.target.indexOf("recstartunix=")>
-1){g=h.target.split("recstartunix=")[1];h.recstartunix=g.split("&")[0];h.target=h.target.replace("recstartunix="+h.recstartunix,"").replace("?&","?").replace("&&","&");if(h.target[h.target.length-1]=="?")h.target=h.target.slice(0,-1)}}g=[{label:"Stream name",type:"str",help:"This may either be a full stream name, a partial wildcard stream name, or a full wildcard stream name.<br>For example, given the stream <i>a</i> you can use: <ul> <li><i>a</i>: the stream configured as <i>a</i></li> <li><i>a+</i>: all streams configured as <i>a</i> with a wildcard behind it, but not <i>a</i> itself</li> <li><i>a+b</i>: only the version of stream <i>a</i> that has wildcard <i>b</i></li> </ul>",
pointer:{main:h,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:t,LTSonly:1},{label:"Target",type:"str",help:"Where the stream will be pushed to.<br> Valid formats: <ul> <li>"+e.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> Valid URL parameters: <ul> <li>recstart=123 - media timestamp in milisseconds where the push should start</li> <li>recstop=456 - media timestamp in miliseconds where the push should stop</li> <li>recstartunix=150000000 - unix time in seconds where the push should start. This will override the recstart parameter.</li> <li>recstopunix=150000000 - unix time in seconds where the push should stop. This will override the recstop parameter.</li> </ul>",
pointer:{main:h,index:"target"},validate:["required",function(a){for(var b in e)if(mist.inputMatch(e[b],a))return false;return{msg:"Does not match a valid target.<br>Valid formats:<ul><li>"+e.join("</li><li>")+"</li></ul>",classes:["red"]}}],LTSonly:1}];c=="auto"&&g.push($("<h4>").text("Optional parameters"),{type:"unix",label:"Schedule time",min:0,help:"The time where the push will become active. The default is to start immediately.",pointer:{main:h,index:"scheduletime"}},{type:"unix",label:"Recording start time",
min:0,help:"Where in the media buffer the recording will start. Defaults to the most recently received keyframe.<br>Only makes sense for live streams.",pointer:{main:h,index:"recstartunix"}},{type:"unix",label:"Complete time",min:0,help:"The time where the push will stop. Defaults to never stop automatically.<br>Only makes sense for live streams.",pointer:{main:h,index:"completetime"}});g.push({type:"buttons",buttons:[{type:"cancel",label:"Cancel","function":function(){UI.navto("Push")}},{type:"save",
label:"Save","function":function(){var b={};h.recstartunix?b.recstartunix="recstartunix="+h.recstartunix:h.scheduletime&&(b.recstartunix="recstartunix="+h.scheduletime);delete h.recstartunix;if(Object.keys(b).length){var d="?",f=h.target.split("?");if(f.length>1){var d="&",f=f[f.length-1],f=f.split("&"),e;for(e in f){var g=f[e].split("=")[0];g in b&&delete b[g]}}if(Object.keys(b).length){d=d+Object.values(b).join("&");h.target=h.target+d}}b={};b[c=="auto"?"push_auto_add":"push_start"]=h;if(typeof a!=
"undefined"&&(a[0]!=h.stream||a[1]!=h.target))b.push_auto_remove=[a];mist.send(function(){UI.navto("Push")},b)}}]});d.append(UI.buildUI(g))}};mist.data.LTS?mist.send(function(a){(t=a.active_streams)||(t=[]);var a=[],b;for(b in t)t[b].indexOf("+")!=-1&&a.push(t[b].replace(/\+.*/,"")+"+");t=t.concat(a);var c=0,d=0;for(b in mist.data.streams){t.push(b);if(mist.inputMatch(UI.findInput("Folder").source_match,mist.data.streams[b].source)){t.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])&&t.push(f+"+"+a.browse.files[e]);d++;if(c==d){t=t.filter(function(a,b,c){return c.lastIndexOf(a)===b}).sort();V()}},{browse:mist.data.streams[b].source},{stream:b});c++}}if(c==d){t=t.filter(function(a,b,c){return c.lastIndexOf(a)===b}).sort();V()}},{active_streams:1}):(t=Object.keys(mist.data.streams),V());break;case "Triggers":"triggers"in
mist.data.config||(mist.data.config.triggers={});y=$("<tbody>");b=$("<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(y);d.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(b);
b.stupidtable();b=mist.data.config.triggers;for(h in b)for(f in b[h])r=triggerRewrite(b[h][f]),y.append($("<tr>").attr("data-index",h+","+f).append($("<td>").text(h)).append($("<td>").text("streams"in r?r.streams.join(", "):"")).append($("<td>").text(r.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":"triggers"in mist.data.config||(mist.data.config.triggers={});c?(c=c.split(","),h=triggerRewrite(mist.data.config.triggers[c[0]][c[1]]),n={triggeron:c[0],appliesto:h.streams,url:h.handler,async:h.sync,
"default":h["default"],params:h.params}):(d.html($("<h2>").text("New Trigger")),n={});d.append(UI.buildUI([{label:"Trigger on",pointer:{main:n,index:"triggeron"},help:"For what event this trigger should activate.",type:"select",select:[["SYSTEM_START","SYSTEM_START: after MistServer boot"],["SYSTEM_STOP","SYSTEM_STOP: right before MistServer shutdown"],["SYSTEM_CONFIG","SYSTEM_CONFIG: after MistServer configurations have changed"],["OUTPUT_START","OUTPUT_START: right after the start command has been send to a protocol"],
["OUTPUT_STOP","OUTPUT_STOP: right after the close command has been send to a protocol "],["STREAM_ADD","STREAM_ADD: right before new stream configured"],["STREAM_CONFIG","STREAM_CONFIG: right before a stream configuration has changed"],["STREAM_REMOVE","STREAM_REMOVE: right before a stream has been deleted"],["STREAM_SOURCE","STREAM_SOURCE: right before stream source is loaded"],["STREAM_LOAD","STREAM_LOAD: right before stream input is loaded in memory"],["STREAM_READY","STREAM_READY: when the stream input is loaded and ready for playback"],
["STREAM_UNLOAD","STREAM_UNLOAD: right before the stream input is removed from memory"],["STREAM_PUSH","STREAM_PUSH: right before an incoming push is accepted"],["STREAM_TRACK_ADD","STREAM_TRACK_ADD: right before a track will be added to a stream; e.g.: additional push received"],["STREAM_TRACK_REMOVE","STREAM_TRACK_REMOVE: right before a track will be removed track from a stream; e.g.: push timeout"],["STREAM_BUFFER","STREAM_BUFFER: when a buffer changes between mostly full or mostly empty"],["RTMP_PUSH_REWRITE",
"RTMP_PUSH_REWRITE: allows rewriting of RTMP push URLs from external to internal representation before further parsing"],["PUSH_OUT_START","PUSH_OUT_START: before recording/pushing, allow target changes."],["RECORDING_END","RECORDING_END: after a recording finishes."],["CONN_OPEN","CONN_OPEN: right after a new incoming connection has been received"],["CONN_CLOSE","CONN_CLOSE: right after a connection has been closed"],["CONN_PLAY","CONN_PLAY: right before a stream playback of a connection"],["USER_NEW",
"USER_NEW: a new user connects that hasn't been allowed or denied access before"],["USER_END","USER_END: a user session disconnects after receiving some media"],["LIVE_BANDWIDTH","LIVE_BANDWIDTH: when the value specified as param is surpassed"]],LTSonly:!0,validate:["required"],"function":function(){switch($(this).getval()){case "SYSTEM_START":case "SYSTEM_STOP":case "SYSTEM_CONFIG":case "OUTPUT_START":case "OUTPUT_STOP":case "RTMP_PUSH_REWRITE":$("[name=appliesto]").setval([]).closest(".UIelement").hide();
$("[name=params]").setval("").closest(".UIelement").hide();break;case "LIVE_BANDWIDTH":$("[name=appliesto]").closest(".UIelement").show();$("[name=params]").closest(".UIelement").show();break;default:$("[name=appliesto]").closest(".UIelement").show();$("[name=params]").setval("").closest(".UIelement").hide()}}},{label:"Applies to",pointer:{main:n,index:"appliesto"},help:"For triggers that can apply to specific streams, this value decides what streams they are triggered for. (none checked = always triggered)",
type:"checklist",checklist:Object.keys(mist.data.streams),LTSonly:!0},$("<br>"),{label:"Handler (URL or executable)",help:"This can be either an HTTP URL or a full path to an executable.",pointer:{main:n,index:"url"},validate:["required"],type:"str",LTSonly:!0},{label:"Blocking",type:"checkbox",help:"If checked, pauses processing and uses the response of the handler. If the response does not start with 1, true, yes or cont, further processing is aborted. If unchecked, processing is never paused and the response is not checked.",
pointer:{main:n,index:"async"},LTSonly:!0},{label:"Parameters",type:"str",help:"The extra data you want this trigger to use.",pointer:{main:n,index:"params"},LTSonly:!0},{label:"Default response",type:"str",help:"The default response in case the handler fails or is set to non-blocking.",pointer:{main:n,index:"default"},LTSonly:!0},{type:"buttons",buttons:[{type:"cancel",label:"Cancel","function":function(){UI.navto("Triggers")}},{type:"save",label:"Save","function":function(){c&&mist.data.config.triggers[c[0]].splice(c[1],
1);var a={handler:n.url,sync:n.async?true:false,streams:typeof n.appliesto=="undefined"?[]:n.appliesto,params:n.params,"default":n["default"]};if(!("triggers"in mist.data.config))mist.data.config.triggers={};n.triggeron in mist.data.config.triggers||(mist.data.config.triggers[n.triggeron]=[]);mist.data.config.triggers[n.triggeron].push(a);mist.send(function(){UI.navto("Triggers")},{config:mist.data.config})}}]}]));$("[name=triggeron]").trigger("change");break;case "Logs":var ta=$("<button>").text("Refresh now").click(function(){$(this).text("Loading..");
mist.send(function(){fa();ta.text("Refresh now")})}).css("padding","0.2em 0.5em").css("flex-grow",0);d.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(){fa()})},
$(this).val()*1E3)},help:"How often the table below should be updated."},{label:"..or",type:"DOMfield",DOMfield:ta,help:"Instantly refresh the table below."}]));d.append($("<button>").text("Purge logs").click(function(){mist.send(function(){mist.data.log=[];UI.navto("Logs")},{clearstatlogs:true})}));y=$("<tbody>").css("font-size","0.9em");d.append($("<table>").addClass("logs").append(y));var va=function(a){var b=$("<span>").text(a);switch(a){case "WARN":b.addClass("orange");break;case "ERROR":case "FAIL":b.addClass("red")}return b},
fa=function(){var a=mist.data.log;if(a){a.length>=2&&a[0][0]<a[a.length-1][0]&&a.reverse();y.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]));y.append($("<tr>").html($("<td>").text(UI.format.dateTime(a[b][0],"long")).css("white-space","nowrap")).append($("<td>").html(va(a[b][1])).css("text-align","center")).append($("<td>").html(c).css("text-align","left")))}}};fa();break;case "Statistics":var B=$("<span>").text("Loading..");
d.append(B);var n={graph:"new"},w=mist.stored.get().graphs?$.extend(!0,{},mist.stored.get().graphs):{},Q={};for(h in mist.data.streams)Q[h]=!0;for(h in mist.data.active_streams)Q[mist.data.active_streams[h]]=!0;var Q=Object.keys(Q).sort(),ga=[];for(h in mist.data.config.protocols)ga.push(mist.data.config.protocols[h].connector);ga.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;B.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:n,index:"graph"},classes:["graph_ids"],"function":function(){if($(this).val()){var a=B.find(".graph_xaxis"),b=B.find(".graph_id");if($(this).val()=="new"){a.children("option").prop("disabled",
false);b.setval("Graph "+(Object.keys(w).length+1)).closest("label").show()}else{var c=w[$(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:n,index:"id"},classes:["graph_id"],validate:[function(a){return a in w?{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:n,index:"xaxis"},value:"time",classes:["graph_xaxis"],"function":function(){$s=B.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"]],pointer:{main:n,index:"datatype"},classes:["graph_datatype"],"function":function(){$s=B.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:",Q],["protocol","The protocol:",ga]],pointer:{main:n,index:"origin"},value:["total"],classes:["graph_origin"]},{type:"buttons",buttons:[{label:"Add data set",type:"save","function":function(){var a;if(n.graph=="new"){a=UI.plot.addGraph(n,b);w[a.id]=a;B.find("input.graph_id").val("");
B.find("select.graph_ids").append($("<option>").text(a.id)).val(a.id).trigger("change")}else a=w[n.graph];var c=UI.plot.datatype.getOptions({datatype:n.datatype,origin:n.origin});a.datasets.push(c);UI.plot.save(a);UI.plot.go(w)}}]}]));var b=$("<div>").addClass("graph_container");d.append(b);var c=B.find("select.graph_ids");for(a in w){var f=UI.plot.addGraph(w[a],b);c.append($("<option>").text(f.id)).val(f.id);var e=[],g;for(g in w[a].datasets){var h=UI.plot.datatype.getOptions({datatype:w[a].datasets[g].datatype,
origin:w[a].datasets[g].origin});e.push(h)}f.datasets=e;w[f.id]=f}c.trigger("change");UI.plot.go(w);UI.interval.set(function(){UI.plot.go(w)},1E4)},{active_streams:!0,capabilities:!0});break;case "Server Stats":if("undefined"==typeof mist.data.capabilities){mist.send(function(){UI.navto(a)},{capabilities:!0});d.append("Loading..");return}var ha=$("<table>"),E=$("<table>"),f={vheader:"CPUs",labels:["Model","Processor speed","Amount of cores","Amount of threads"],content:[]};for(h in mist.data.capabilities.cpu)b=
mist.data.capabilities.cpu[h],f.content.push({header:"CPU #"+(Number(h)+1),body:[b.model,UI.format.addUnit(UI.format.number(b.mhz),"MHz"),b.cores,b.threads]});h=UI.buildVheaderTable(f);var ua=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);ha.replaceWith(a);ha=a;b={vheader:"Load average",labels:["CPU use","1 minute","5 minutes","15 minutes"],content:[{header:"&nbsp;",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);E.replaceWith(b);E=b};ua();d.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(ha)).append($("<td>").append(E))).append($("<tr>").append($("<td>").append(h).attr("colspan",2))));UI.interval.set(function(){mist.send(function(){ua()},
{capabilities:true})},3E4);break;case "Email for Help":h=$.extend({},mist.data);delete h.statistics;delete h.totals;delete h.clients;delete h.capabilities;h=JSON.stringify(h);h="Version: "+mist.data.config.version+"\n\nConfig:\n"+h;n={};d.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:n,index:"name"},
value:mist.user.name},{type:"email",label:"Your email address",validate:["required"],pointer:{main:n,index:"email"}},{type:"hidden",value:"Integrated Help",pointer:{main:n,index:"subject"}},{type:"hidden",value:"-",pointer:{main:n,index:"company"}},{type:"textarea",rows:20,label:"Your message",validate:["required"],pointer:{main:n,index:"message"}},{type:"textarea",rows:20,label:"Your config file",readonly:!0,value:h,pointer:{main:n,index:"configfile"}},{type:"buttons",buttons:[{type:"save",label:"Send",
"function":function(a){$(a).text("Sending..");$.ajax({type:"POST",url:"https://mistserver.org/contact?skin=plain",data:n,success:function(a){a=$("<span>").html(a);a.find("script").remove();d.html(a[0].innerHTML)}})}}]}]));break;case "Disconnect":mist.user.password="";delete mist.user.authstring;delete mist.user.loggedin;sessionStorage.removeItem("mistLogin");UI.navto("Login");break;default:d.append($("<p>").text("This tab does not exist."))}d.find(".field").filter(function(){var a=$(this).getval();
return a==""||a==null?true:false}).each(function(){var a=[];$(this).is("input, select, textarea")?a.push($(this)):a=$(this).find("input, select, textarea");if(a.length){$(a[0]).focus();return false}})}};"origin"in location||(location.origin=location.protocol+"//");var host;host="file://"==location.origin?"http://localhost:4242/api":location.origin+location.pathname.replace(/\/+$/,"")+"/api";
var mist={data:{},user:{name:"",password:"",host:host},send:function(a,c,d){var c=c||{},d=d||{},d=$.extend(true,{timeOut:3E4,sendData:c},d),b={authorize:{password:mist.user.authstring?MD5(mist.user.password+mist.user.authstring):"",username:mist.user.name}};$.extend(true,b,c);log("Send",$.extend(true,{},c));var e={url:mist.user.host,type:"POST",data:{command:JSON.stringify(b)},dataType:"jsonp",crossDomain:true,timeout:d.timeout*1E3,async:true,error:function(b,e){delete mist.user.loggedin;if(!d.hide){switch(e){case "timeout":e=
$("<i>").text("The connection timed out. ");break;case "abort":e=$("<i>").text("The connection was aborted. ");break;default:e=$("<i>").text(e+". ").css("text-transform","capitalize")}$("#message").addClass("red").text("An error occurred while attempting to communicate with MistServer:").append($("<br>")).append($("<span>").text(e)).append($("<a>").text("Send server request again").click(function(){mist.send(a,c,d)}))}UI.navto("Login")},success:function(e){log("Receive",$.extend(true,{},e),"as reply to",
d.sendData);delete mist.user.loggedin;switch(e.authorize.status){case "OK":if("streams"in e)if(e.streams)if("incomplete list"in e.streams){delete e.streams["incomplete list"];$.extend(mist.data.streams,e.streams)}else mist.data.streams=e.streams;else mist.data.streams={};var g=$.extend({},e),h=["config","capabilities","ui_settings","LTS","active_streams","browse","log","totals","bandwidth"],o;for(o in g)h.indexOf(o)==-1&&delete g[o];if("bandwidth"in b&&!("bandwidth"in e))g.bandwidth=null;$.extend(mist.data,
g);mist.user.loggedin=true;UI.elements.connection.status.text("Connected").removeClass("red").addClass("green");UI.elements.connection.user_and_host.text(mist.user.name+" @ "+mist.user.host);UI.elements.connection.msg.removeClass("red").text("Last communication with the server at "+UI.format.time((new Date).getTime()/1E3));e.LTS&&UI.elements.menu.find(".LTSonly").removeClass("LTSonly");if(e.log){g=e.log[e.log.length-1];UI.elements.connection.msg.append($("<br>")).append($("<span>").text("Last log entry: "+
UI.format.time(g[0])+" ["+g[1]+"] "+g[2]))}if("totals"in e){g=function(a,b,c){var d;d=function(){for(var a in c.fields)e[c.fields[a]].push([m,0])};var e={},g;for(g in c.fields)e[c.fields[g]]=[];var h=0,m;if(c.data){if(c.start>mist.data.config.time-600){m=(mist.data.config.time-600)*1E3;d();m=c.start*1E3;d()}else m=c.start*1E3;for(g in c.data){if(g==0){m=c.start*1E3;var o=0}else{m=m+c.interval[o][1]*1E3;c.interval[o][0]--;if(c.interval[o][0]<=0){o++;o<c.interval.length-1&&(h=h+2)}}if(h%2==1){d();h--}for(var q in c.data[g])e[c.fields[q]].push([m,
c.data[g][q]]);if(h){d();h--}}if(mist.data.config.time-c.end>20){d();m=(mist.data.config.time-15)*1E3;d()}}else{m=(mist.data.config.time-600)*1E3;d();m=(mist.data.config.time-15)*1E3;d()}d=e;stream=a?a.join(" "):"all_streams";protocol=b?b.join("_"):"all_protocols";stream in mist.data.totals||(mist.data.totals[stream]={});protocol in mist.data.totals[stream]||(mist.data.totals[stream][protocol]={});$.extend(mist.data.totals[stream][protocol],d)};mist.data.totals={};if("fields"in e.totals)g(c.totals.streams,
c.totals.protocols,e.totals);else for(o in e.totals)g(c.totals[o].streams,c.totals[o].protocols,e.totals[o])}a&&a(e,d);break;case "CHALL":if(e.authorize.challenge==mist.user.authstring){mist.user.password!=""&&UI.elements.connection.msg.text("The credentials you provided are incorrect.").addClass("red");UI.navto("Login")}else if(mist.user.password=="")UI.navto("Login");else{mist.user.authstring=e.authorize.challenge;mist.send(a,c,d);sessionStorage.setItem("mistLogin",JSON.stringify({host:mist.user.host,
name:mist.user.name,password:mist.user.password}))}break;case "NOACC":UI.navto("Create a new account");break;case "ACC_MADE":delete c.authorize;mist.send(a,c,d);break;default:UI.navto("Login")}}};d.hide||UI.elements.connection.msg.removeClass("red").text("Data sent, waiting for a reply..").append($("<br>")).append($("<a>").text("Cancel request").click(function(){h.abort()}));var h=$.ajax(e)},inputMatch:function(a,c){if(typeof a=="undefined")return false;typeof a=="string"&&(a=[a]);for(var d in a){var b=
a[d].replace(/[^\w\s]/g,"\\$&"),b=b.replace(/\\\*/g,".*");if(RegExp("^(?:[a-zA-Z]:)?"+b+"(?:\\?[^\\?]*)?$","i").test(c))return true}return false},convertBuildOptions:function(a,c){var d=[],b=["required","optional"];"desc"in a&&d.push({type:"help",help:a.desc});for(var e in b)if(a[b[e]]){d.push($("<h4>").text(UI.format.capital(b[e])+" parameters"));for(var h in a[b[e]]){var m=a[b[e]][h],g={label:UI.format.capital(m.name),pointer:{main:c,index:h},validate:[]};b[e]=="required"&&(!("default"in m)||m["default"]==
"")&&g.validate.push("required");if("default"in m){g.placeholder=m["default"];if(m.type=="select")for(var q in m.select)if(m.select[q][0]==m["default"]){g.placeholder=m.select[q][1];break}}if("help"in m)g.help=m.help;if("unit"in m)g.unit=m.unit;if("type"in m)switch(m.type){case "int":g.type="int";break;case "uint":g.type="int";g.min=0;break;case "debug":g.type="debug";break;case "select":g.type="select";g.select=m.select.slice(0);g.select.unshift(["","Default"+("placeholder"in g?" ("+g.placeholder+
")":"")]);break;default:g.type="str"}else g.type="checkbox";d.push(g)}}return d},stored:{get:function(){return mist.data.ui_settings||{}},set:function(a,c){var d=this.get();d[a]=c;mist.send(function(){},{ui_settings:d})},del:function(a){delete mist.data.ui_settings[a];mist.send(function(){},{ui_settings:mist.data.ui_settings})}}};
function log(){try{UI.debug&&[].push.call(arguments,Error().stack);[].unshift.call(arguments,"["+UI.format.time((new Date).getTime()/1E3)+"]");console.log.apply(console,arguments)}catch(a){}}
$.fn.getval=function(){var a=$(this).data("opts"),c=$(this).val();if(a&&"type"in a)switch(a.type){case "span":c=$(this).html();break;case "checkbox":c=$(this).prop("checked");break;case "radioselect":a=$(this).find("label > input[type=radio]:checked").parent();if(a.length){c=[];c.push(a.children("input[type=radio]").val());a=a.children("select");a.length&&c.push(a.val())}else c="";break;case "checklist":c=[];$(this).find(".checklist input[type=checkbox]:checked").each(function(){c.push($(this).attr("name"))});
break;case "unix":c!=""&&(c=Math.round(new Date($(this).val())/1E3));break;case "selectinput":c=$(this).children("select").first().val();c=="CUSTOM"&&(c=$(this).children("label").first().find(".field_container").children().first().getval());break;case "inputlist":c=[];$(this).children().each(function(){$(this).val()!=""&&c.push($(this).val())})}return c};
$.fn.setval=function(a){var c=$(this).data("opts");$(this).val(a);if(c&&"type"in c)switch(c.type){case "span":$(this).html(a);break;case "checkbox":$(this).prop("checked",a);break;case "geolimited":case "hostlimited":c=$(this).closest(".field_container").data("subUI");if(typeof a=="undefined"||a.length==0)a="-";c.blackwhite.val(a.charAt(0));var a=a.substr(1).split(" "),d;for(d in a)c.values.append(c.prototype.clone(true).val(a[d]));c.blackwhite.trigger("change");break;case "radioselect":if(typeof a==
"undefined")return $(this);d=$(this).find('label > input[type=radio][value="'+a[0]+'"]').prop("checked",true).parent();a.length>1&&d.children("select").val(a[1]);break;case "checklist":c=$(this).find(".checklist input[type=checkbox]").prop("checked",false);for(d in a)c.filter('[name="'+a[d]+'"]').prop("checked",true);break;case "unix":if(typeof a!="undefined"){a=new Date(Math.round(a)*1E3);a.setMinutes(a.getMinutes()-a.getTimezoneOffset());a=a.toISOString();$(this).val(a.split("Z")[0])}break;case "selectinput":a===
null&&(a="");var b=false;for(d in c.selectinput){var e;typeof c.selectinput[d]=="string"?e=c.selectinput[d]:typeof c.selectinput[d][0]=="string"&&(e=c.selectinput[d][0]);if(e==a){$(this).children("select").first().val(a);b=true;break}}if(!b){$(this).children("label").first().find(".field_container").children().first().setval(a);$(this).children("select").first().val("CUSTOM").trigger("change")}break;case "inputlist":for(d in a)$(this).append($(this).data("newitem")().val(a[d]));$(this).append($(this).children().first())}$(this).trigger("change");
return $(this)};function parseURL(a){var c=document.createElement("a");c.href=a;return{protocol:c.protocol+"//",host:c.hostname,port:c.port?":"+c.port:""}}function triggerRewrite(a){return typeof a=="object"&&typeof a.length=="undefined"?a:obj={handler:a[0],sync:a[1],streams:a[2],"default":a[3]}};

File diff suppressed because it is too large Load diff

View file

@ -1,327 +0,0 @@
function mistembed(streamname) {
//find the current script
var me;
if (('currentScript' in document) && (document.currentScript)) {
me = document.currentScript;
//not supported in old browsers :(
}
else {
var scripts = document.getElementsByTagName('script');
me = scripts[scripts.length - 1];
//not correct if the script is inserted dynamically, but this is how it used to be
}
// return the current flash version
function flash_version() {
var version = 0;
try {
// check in the mimeTypes
version = navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin.description.replace(/([^0-9\.])/g, '').split('.')[0];
} catch(e){}
try {
// for our special friend IE
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable("$version").replace(/([^0-9\,])/g, '').split(',')[0];
} catch(e){}
return parseInt(version, 10);
};
// return true if silverlight is installed
function silverlight_installed() {
var plugin;
try {
// check in the mimeTypes
plugin = navigator.plugins["Silverlight Plug-In"];
return !!plugin;
} catch(e){}
try {
// for our special friend IE
plugin = new ActiveXObject('AgControl.AgControl');
return true;
} catch(e){}
return false;
};
// return true if the browser thinks it can play the mimetype
function html5_video_type(type) {
var support = false;
if (type == 'video/mp4') {
if ((navigator.userAgent.indexOf('MSIE') > -1) && (parseInt(navigator.userAgent.split('MSIE')[1]) <= 9)) {
//IE <= 9 doesn't support MP4, Firefox seems to correctly see it now.
return false;
}
if (navigator.userAgent.indexOf('Firefox') > -1) {
//firefox claims to support MP4 but doesn't when:
// - under win xp
// - the stream is live
if (video.type == 'live') {
return false;
}
//find "Windows NT X;" in user userAgent
//sorry, I don't like regexes.. I avoid them when I can :$
var s = navigator.userAgent.split('Windows NT ');
if (s.length > 1) {
s = s[1].split(';');
s = s[0];
if (Number(s) <= 5.1) {
return false;
}
}
}
}
try {
var v = document.createElement('video');
if( v && v.canPlayType(type) != "" )
{
support = true; // true-ish, anyway
}
} catch(e){}
return support;
}
//return true if rtsp is supported
function rtsp_support() {
var plugin;
try {
// check in the mimeTypes
plugin = navigator.mimeTypes["application/x-google-vlc-plugin"];
return !!plugin;
} catch(e){}
try {
// for our special friend IE
plugin = new ActiveXObject('VideoLAN.Vlcplugin.1');
return true;
} catch(e){}
return false;
}
// parse a "type" string from the controller. Format:
// xxx/# (e.g. flash/3) or xxx/xxx/xxx (e.g. html5/application/ogg)
function parseType(type) {
var split = type.split('/');
if( split.length > 2 ) {
split[1] += '/' + split[2];
}
return split;
}
// return true if a type is supported
function hasSupport(type) {
var typemime = parseType(type);
switch(typemime[0]) {
case 'flash': return flash_version() >= parseInt(typemime[1], 10); break;
case 'html5': return html5_video_type(typemime[1]); break;
case 'rtsp': return rtsp_support(); break;
case 'silverlight': return silverlight_installed(); break;
default: return false; break;
}
}
// build HTML for certain kinds of types
function buildPlayer(src, container, videowidth, videoheight, vtype) {
// used to recalculate the width/height
var ratio;
// get the container's width/height
var containerwidth = parseInt(container.clientWidth, 10);
var containerheight = parseInt(container.clientHeight, 10);
if(videowidth > containerwidth && containerwidth > 0) {
ratio = videowidth / containerwidth;
videowidth /= ratio;
videoheight /= ratio;
}
if(videoheight > containerheight && containerheight > 0) {
ratio = videoheight / containerheight;
videowidth /= ratio;
videoheight /= ratio;
}
var maintype = parseType(src.type);
mistvideo[streamname].embedded = src;
switch(maintype[0]) {
case 'flash':
// maintype[1] is already checked (i.e. user has version > maintype[1])
var flashplayer,
url = encodeURIComponent(src.url) + '&controlBarMode=floating&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3' + (vtype == 'live' ? "&streamType=live" : "") + (autoplay ? '&autoPlay=true' : '');
/*
if( parseInt(maintype[1], 10) >= 10 ) {
flashplayer = 'http://fpdownload.adobe.com/strobe/FlashMediaPlayback_101.swf';
}
else {
flashplayer = 'http://fpdownload.adobe.com/strobe/FlashMediaPlayback.swf';
}
*/
flashplayer = src.player_url;
container.innerHTML += '<object width="' + videowidth + '" height="' + videoheight + '">' +
'<param name="movie" value="' + flashplayer + '"></param>' +
'<param name="flashvars" value="src=' + url + '"></param>' +
'<param name="allowFullScreen" value="true"></param>' +
'<param name="allowscriptaccess" value="always"></param>' +
'<param name="wmode" value="direct"></param>' +
(autoplay ? '<param name="autoPlay" value="true">' : '') +
'<embed src="' + flashplayer + '" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="' + videowidth + '" height="' + videoheight + '" flashvars="src=' + url + '"></embed>' +
'</object>';
break;
case 'html5':
container.innerHTML += '<video width="' + videowidth + '" height="' + videoheight + '" src="' + src.url + '" controls="controls" '+(autoplay ? 'autoplay="autoplay"' : '')+'><strong>No HTML5 video support</strong></video>';
break;
case 'rtsp':
/*container.innerHTML += '<object classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width="'+videowidth+'" height="'+videoheight+'">'+
'<param name="src" value="'+encodeURI(src.url)+'">'+
'<param name="console" value="video1">'+
'<param name="controls" value="All">'+
'<param name="autostart" value="false">'+
'<param name="loop" value="false">'+
'<embed name="myMovie" src="'+encodeURI(src.url)+'" width="'+videowidth+'" height="'+videoheight+'" autostart="false" loop="false" nojava="true" console="video1" controls="All"></embed>'+
'<noembed>Something went wrong.</noembed>'+
'</object>'; //realplayer, doesnt work */
container.innerHTML += '<embed type="application/x-google-vlc-plugin"'+
'pluginspage="http://www.videolan.org"'+
'width="'+videowidth+'"'+
'height="'+videoheight+'"'+
'target="'+encodeURI(src.url)+'"'+
'autoplay="'+(autoplay ? 'yes' : 'no')+'"'+
'>'+
'</embed>'+
'<object classid="clsid:9BE31822-FDAD-461B-AD51-BE1D1C159921" codebase="http://downloads.videolan.org/pub/videolan/vlc/latest/win32/axvlc.cab">'+
'</object>'; //vlc, seems to work, sort of. it's trying anyway
break;
case 'silverlight':
container.innerHTML += '<object data="data:application/x-silverlight," type="application/x-silverlight" width="' + videowidth + '" height="' + videoheight + '">'+
'<param name="source" value="' + encodeURI(src.url) + '/player.xap"/>'+
'<param name="onerror" value="onSilverlightError" />'+
'<param name="autoUpgrade" value="true" />'+
'<param name="background" value="white" />'+
'<param name="enableHtmlAccess" value="true" />'+
'<param name="minRuntimeVersion" value="3.0.40624.0" />'+
'<param name="initparams" value =\'autoload=false,'+(autoplay ? 'autoplay=true' : 'autoplay=false')+',displaytimecode=false,enablecaptions=true,joinLive=true,muted=false,playlist=<playList><playListItems><playListItem title="Test" description="testing" mediaSource="' + encodeURI(src.url) + '" adaptiveStreaming="true" thumbSource="" frameRate="25.0" width="" height=""></playListItem></playListItems></playList>\' />'+
'<a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;"> <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none" /></a>'+
'</object>';
break;
default:
container.innerHTML += '<strong>Missing embed code for output type "'+src.type+'"</strong>';
video.error = 'Missing embed code for output type "'+src.type;
}
}
var video = mistvideo[streamname],
container = document.createElement('div'),
forceType = false,
forceSupportCheck = false,
autoplay = true,
urlappend = false;
if (me.parentNode.hasAttribute('data-forcetype')) {
forceType = me.parentNode.getAttribute('data-forcetype');
}
if (me.parentNode.hasAttribute('data-forcesupportcheck')) {
forceSupportCheck = true;
}
if (me.parentNode.hasAttribute('data-noautoplay')) {
autoplay = false;
}
if (me.parentNode.hasAttribute('data-urlappend')) {
urlappend = me.parentNode.getAttribute('data-urlappend');
}
if (video.width == 0) { video.width = 250; }
if (video.height == 0) { video.height = 250; }
// create the container
me.parentNode.insertBefore(container, me);
// set the class to 'mistvideo'
container.setAttribute('class', 'mistvideo');
// remove script tag
me.parentNode.removeChild(me);
if(video.error) {
// there was an error; display it
if (video.on_error){
container.innerHTML = video.on_error;
}else{
container.innerHTML = ['<strong>Error: ', video.error, '</strong>'].join('');
}
}
else if ((typeof video.source == 'undefined') || (video.source.length < 1)) {
// no stream sources
if (video.on_error){
container.innerHTML = video.on_error;
}else{
container.innerHTML = '<strong>Error: no active source or compatible protocols for this stream</strong>';
}
}
else {
// no error, and sources found. Check the video types and output the best
// available video player.
var i,
vtype = (video.type ? video.type : 'unknown'),
foundPlayer = false,
len = video.source.length;
for (var i in video.source) {
var support = hasSupport(video.source[i].type);
video.source[i].browser_support = support;
if ((support) || (forceType)) {
if ((!forceType) || ((forceType) && (video.source[i].type.indexOf(forceType) >= 0))) {
if (foundPlayer === false) {
foundPlayer = i;
if (!forceSupportCheck) {
break;
}
}
}
}
}
if (foundPlayer === false) {
// of all the streams given, none was supported (eg. no flash and HTML5 video). Display error
container.innerHTML = '<strong>No support for any player found</strong>';
}
else {
// we support this kind of video, so build it.
var source = video.source[foundPlayer];
if (urlappend) {
source.url += urlappend;
source.relurl += urlappend;
}
else if (me.src.indexOf('?') != -1) {
var params = me.src.split('?');
params.shift();
params.join('?');
source.url += '?'+params;
source.relurl += '?'+params;
}
buildPlayer(source, container, video.width, video.height, vtype);
}
}
return (mistvideo[streamname].embedded ? mistvideo[streamname].embedded.type : false);
//keep empty line at end of file
}

View file

@ -123,9 +123,12 @@ namespace Mist {
capa["url_match"].append("/info_$.js"); capa["url_match"].append("/info_$.js");
capa["url_match"].append("/json_$.js"); capa["url_match"].append("/json_$.js");
capa["url_match"].append("/player.js"); capa["url_match"].append("/player.js");
capa["url_match"].append("/player.css");
capa["url_match"].append("/videojs.js"); capa["url_match"].append("/videojs.js");
capa["url_match"].append("/dashjs.js"); capa["url_match"].append("/dashjs.js");
capa["url_match"].append("/webrtc.js");
capa["url_match"].append("/skins/default.css");
capa["url_match"].append("/skins/dev.css");
capa["url_match"].append("/skins/videojs.css");
capa["url_match"].append("/embed_$.js"); capa["url_match"].append("/embed_$.js");
capa["url_match"].append("/flashplayer.swf"); capa["url_match"].append("/flashplayer.swf");
capa["url_match"].append("/oldflashplayer.swf"); capa["url_match"].append("/oldflashplayer.swf");
@ -133,15 +136,11 @@ namespace Mist {
capa["optional"]["wrappers"]["help"] = "Which players are attempted and in what order."; capa["optional"]["wrappers"]["help"] = "Which players are attempted and in what order.";
capa["optional"]["wrappers"]["default"] = ""; capa["optional"]["wrappers"]["default"] = "";
capa["optional"]["wrappers"]["type"] = "ord_multi_sel"; capa["optional"]["wrappers"]["type"] = "ord_multi_sel";
/*capa["optional"]["wrappers"]["allowed"].append("theoplayer");
capa["optional"]["wrappers"]["allowed"].append("jwplayer");*/
capa["optional"]["wrappers"]["allowed"].append("html5"); capa["optional"]["wrappers"]["allowed"].append("html5");
capa["optional"]["wrappers"]["allowed"].append("videojs"); capa["optional"]["wrappers"]["allowed"].append("videojs");
capa["optional"]["wrappers"]["allowed"].append("dashjs"); capa["optional"]["wrappers"]["allowed"].append("dashjs");
//capa["optional"]["wrappers"]["allowed"].append("polytrope"); //currently borked capa["optional"]["wrappers"]["allowed"].append("webrtc");
capa["optional"]["wrappers"]["allowed"].append("flash_strobe"); capa["optional"]["wrappers"]["allowed"].append("flash_strobe");
capa["optional"]["wrappers"]["allowed"].append("silverlight");
capa["optional"]["wrappers"]["allowed"].append("img");
capa["optional"]["wrappers"]["option"] = "--wrappers"; capa["optional"]["wrappers"]["option"] = "--wrappers";
capa["optional"]["wrappers"]["short"] = "w"; capa["optional"]["wrappers"]["short"] = "w";
cfg->addConnectorOptions(8080, capa); cfg->addConnectorOptions(8080, capa);
@ -326,6 +325,10 @@ namespace Mist {
} }
/*LTS-END*/ /*LTS-END*/
std::string uAgent = H.GetHeader("User-Agent"); std::string uAgent = H.GetHeader("User-Agent");
std::string devSkin = "";
if (H.GetVar("dev").size()) { devSkin = ",skin:\"dev\""; }
H.Clean(); H.Clean();
H.SetHeader("Content-Type", "text/html"); H.SetHeader("Content-Type", "text/html");
H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
@ -339,7 +342,7 @@ namespace Mist {
std::string hlsUrl = "/hls/"+streamName+"/index.m3u8"; std::string hlsUrl = "/hls/"+streamName+"/index.m3u8";
std::string mp4Url = "/"+streamName+".mp4"; std::string mp4Url = "/"+streamName+".mp4";
H.SetBody("<!DOCTYPE html><html><head><title>"+streamName+"</title><style>body{color:white;background:black;}</style></head><body><div class=mistvideo id=\""+streamName+"\"><noscript><video controls autoplay><source src=\""+hlsUrl+"\" type=\"application/vnd.apple.mpegurl\"><source src=\""+mp4Url+"\" type=\"video/mp4\"><a href=\""+hlsUrl+"\">Click here to play the video [Apple]</a><br><a href=\""+mp4Url+"\">Click here to play the video [MP4]</a></video></noscript><script src=\"player.js\"></script><script>mistPlay('"+streamName+"',{host:'"+fullURL.getUrl()+"',target:document.getElementById('"+streamName+"')})</script></div></body></html>"); H.SetBody("<!DOCTYPE html><html><head><title>"+streamName+"</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><style>html{margin:0;padding:0;display:table;width:100%;height:100%;}body{color:white;background:#0f0f0f;margin:0;padding:0;display:table-cell;vertical-align:middle;text-align:center}body>div>div{text-align:left;}</style></head><body><div class=mistvideo id=\""+streamName+"\"><noscript><video controls autoplay><source src=\""+hlsUrl+"\" type=\"application/vnd.apple.mpegurl\"><source src=\""+mp4Url+"\" type=\"video/mp4\"><a href=\""+hlsUrl+"\">Click here to play the video [Apple]</a><br><a href=\""+mp4Url+"\">Click here to play the video [MP4]</a></video></noscript><script src=\"player.js\"></script><script>mistPlay('"+streamName+"',{host:'"+fullURL.getUrl()+"',target:document.getElementById('"+streamName+"')"+devSkin+"})</script></div></body></html>");
if ((uAgent.find("iPad") != std::string::npos) || (uAgent.find("iPod") != std::string::npos) || (uAgent.find("iPhone") != std::string::npos)) { if ((uAgent.find("iPad") != std::string::npos) || (uAgent.find("iPod") != std::string::npos) || (uAgent.find("iPhone") != std::string::npos)) {
H.SetHeader("Location",hlsUrl); H.SetHeader("Location",hlsUrl);
H.SendResponse("307", "HLS redirect", myConn); H.SendResponse("307", "HLS redirect", myConn);
@ -613,7 +616,7 @@ namespace Mist {
return; return;
} }
if ((H.url.length() > 9 && H.url.substr(0, 6) == "/info_" && H.url.substr(H.url.length() - 3, 3) == ".js") || (H.url.length() > 10 && H.url.substr(0, 7) == "/embed_" && H.url.substr(H.url.length() - 3, 3) == ".js") || (H.url.length() > 9 && H.url.substr(0, 6) == "/json_" && H.url.substr(H.url.length() - 3, 3) == ".js")){ if ((H.url.length() > 9 && H.url.substr(0, 6) == "/info_" && H.url.substr(H.url.length() - 3, 3) == ".js") || (H.url.length() > 9 && H.url.substr(0, 6) == "/json_" && H.url.substr(H.url.length() - 3, 3) == ".js")){
if (websocketHandler()){return;} if (websocketHandler()){return;}
std::string reqHost = HTTP::URL(H.GetHeader("Host")).host; std::string reqHost = HTTP::URL(H.GetHeader("Host")).host;
std::string useragent = H.GetVar("ua"); std::string useragent = H.GetVar("ua");
@ -645,24 +648,13 @@ namespace Mist {
}else{ }else{
response = json_resp.toString(); response = json_resp.toString();
} }
if (rURL.substr(0, 7) == "/embed_" && !json_resp.isMember("error")){
#include "embed.js.h"
response.append("\n(");
if (embed_js[embed_js_len - 2] == ';'){//check if we have a trailing ;\n or just \n
response.append((char*)embed_js, (size_t)embed_js_len - 2); //remove trailing ";\n" from xxd conversion
}else{
response.append((char*)embed_js, (size_t)embed_js_len - 1); //remove trailing "\n" from xxd conversion
}
response.append("(\"" + streamName + "\"));\n");
}
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean(); H.Clean();
return; return;
} //embed code generator } //embed code generator
if ((H.url == "/player.js") || ((H.url.substr(0, 7) == "/embed_") && (H.url.length() > 10) && (H.url.substr(H.url.length() - 3, 3) == ".js"))){
if (H.url == "/player.js"){
HTTP::URL fullURL(H.GetHeader("Host")); HTTP::URL fullURL(H.GetHeader("Host"));
if (!fullURL.protocol.size()){ if (!fullURL.protocol.size()){
fullURL.protocol = getProtocolForPort(fullURL.getPort()); fullURL.protocol = getProtocolForPort(fullURL.getPort());
@ -681,7 +673,7 @@ namespace Mist {
H.Clean(); H.Clean();
H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
H.setCORSHeaders(); H.setCORSHeaders();
H.SetHeader("Content-Type", "application/javascript"); H.SetHeader("Content-Type", "application/javascript; charset=utf-8");
if(method == "OPTIONS" || method == "HEAD"){ if(method == "OPTIONS" || method == "HEAD"){
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean(); H.Clean();
@ -689,8 +681,10 @@ namespace Mist {
} }
response.append("if (typeof mistoptions == 'undefined') { mistoptions = {}; }\nif (!('host' in mistoptions)) { mistoptions.host = '"+fullURL.getUrl()+"'; }\n"); response.append("if (typeof mistoptions == 'undefined') { mistoptions = {}; }\nif (!('host' in mistoptions)) { mistoptions.host = '"+fullURL.getUrl()+"'; }\n");
#include "core.js.h"
response.append((char*)core_js, (size_t)core_js_len); #include "player.js.h"
response.append((char*)player_js, (size_t)player_js_len);
jsonForEach(config->getOption("wrappers",true),it){ jsonForEach(config->getOption("wrappers",true),it){
bool used = false; bool used = false;
if (it->asStringRef() == "html5"){ if (it->asStringRef() == "html5"){
@ -703,26 +697,6 @@ namespace Mist {
response.append((char*)flash_strobe_js, (size_t)flash_strobe_js_len); response.append((char*)flash_strobe_js, (size_t)flash_strobe_js_len);
used = true; used = true;
} }
if (it->asStringRef() == "silverlight"){
#include "silverlight.js.h"
response.append((char*)silverlight_js, (size_t)silverlight_js_len);
used = true;
}
if (it->asStringRef() == "theoplayer"){
#include "theoplayer.js.h"
response.append((char*)theoplayer_js, (size_t)theoplayer_js_len);
used = true;
}
if (it->asStringRef() == "jwplayer"){
#include "jwplayer.js.h"
response.append((char*)jwplayer_js, (size_t)jwplayer_js_len);
used = true;
}
if (it->asStringRef() == "polytrope"){
#include "polytrope.js.h"
response.append((char*)polytrope_js, (size_t)polytrope_js_len);
used = true;
}
if (it->asStringRef() == "dashjs"){ if (it->asStringRef() == "dashjs"){
#include "dashjs.js.h" #include "dashjs.js.h"
response.append((char*)dash_js, (size_t)dash_js_len); response.append((char*)dash_js, (size_t)dash_js_len);
@ -733,9 +707,9 @@ namespace Mist {
response.append((char*)video_js, (size_t)video_js_len); response.append((char*)video_js, (size_t)video_js_len);
used = true; used = true;
} }
if (it->asStringRef() == "img"){ if (it->asStringRef() == "webrtc"){
#include "img.js.h" #include "webrtc.js.h"
response.append((char*)img_js, (size_t)img_js_len); response.append((char*)webrtc_js, (size_t)webrtc_js_len);
used = true; used = true;
} }
if (!used) { if (!used) {
@ -743,6 +717,10 @@ namespace Mist {
} }
} }
if ((rURL.substr(0, 7) == "/embed_") && (rURL.length() > 10) && (rURL.substr(rURL.length() - 3, 3) == ".js")){
response.append("var container = document.createElement(\"div\");\ncontainer.id = \""+streamName+"\";\ndocument.write(container.outerHTML);\nmistPlay(\""+streamName+"\",{target:document.getElementById(\""+streamName+"\")});");
}
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean(); H.Clean();
@ -750,8 +728,9 @@ namespace Mist {
} }
if (H.url == "/player.css"){ if (H.url.substr(0, 7) == "/skins/"){
std::string response; std::string response;
std::string url = H.url;
H.Clean(); H.Clean();
H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
H.setCORSHeaders(); H.setCORSHeaders();
@ -762,8 +741,24 @@ namespace Mist {
return; return;
} }
#include "mist.css.h" if (url == "/skins/default.css") {
response.append((char*)mist_css, (size_t)mist_css_len); #include "skin_default.css.h"
response.append((char*)skin_default_css, (size_t)skin_default_css_len);
}
else if (url == "/skins/dev.css") {
#include "skin_dev.css.h"
response.append((char*)skin_dev_css, (size_t)skin_dev_css_len);
}
else if (url == "/skins/videojs.css") {
#include "skin_videojs.css.h"
response.append((char*)skin_videojs_css, (size_t)skin_videojs_css_len);
}
else {
H.SetBody("Unknown stylesheet: "+url);
H.SendResponse("404", "Unknown stylesheet", myConn);
H.Clean();
return;
}
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
@ -782,10 +777,8 @@ namespace Mist {
return; return;
} }
#include "playervideo.js.h" #include "player_video.js.h"
response.append((char*)playervideo_js, (size_t)playervideo_js_len); response.append((char*)player_video_js, (size_t)player_video_js_len);
#include "playerhlsvideo.js.h"
response.append((char*)playerhlsvideo_js, (size_t)playerhlsvideo_js_len);
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
@ -804,10 +797,30 @@ namespace Mist {
return; return;
} }
#include "playerdashlic.js.h" #include "player_dash_lic.js.h"
response.append((char*)playerdashlic_js, (size_t)playerdashlic_js_len); response.append((char*)player_dash_lic_js, (size_t)player_dash_lic_js_len);
#include "playerdash.js.h" #include "player_dash.js.h"
response.append((char*)playerdash_js, (size_t)playerdash_js_len); response.append((char*)player_dash_js, (size_t)player_dash_js_len);
H.SetBody(response);
H.SendResponse("200", "OK", myConn);
H.Clean();
return;
}
if (H.url == "/webrtc.js"){
std::string response;
H.Clean();
H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
H.setCORSHeaders();
H.SetHeader("Content-Type", "application/javascript");
if (method == "OPTIONS" || method == "HEAD"){
H.SendResponse("200", "OK", myConn);
H.Clean();
return;
}
#include "player_webrtc.js.h"
response.append((char*)player_webrtc_js, (size_t)player_webrtc_js_len);
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);