diff --git a/node/bindata.go b/node/bindata.go index 6f61aa9d60746d36a0570a4822fcbe0644dba45b..eb3344a8e6bd92b58af7acedb855926d63e999e2 100644 --- a/node/bindata.go +++ b/node/bindata.go @@ -112,7 +112,7 @@ func staticAutoradioSvg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/autoradio.svg", size: 3430, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/autoradio.svg", size: 3430, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -129,7 +129,7 @@ func staticAutoradioSvgBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/autoradio.svg.br", size: 1417, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/autoradio.svg.br", size: 1417, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -146,7 +146,7 @@ func staticAutoradioSvgGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/autoradio.svg.gz", size: 1538, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/autoradio.svg.gz", size: 1538, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -169,7 +169,7 @@ func staticCssBootstrapMinCss() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/bootstrap.min.css", size: 159515, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/css/bootstrap.min.css", size: 159515, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -186,7 +186,7 @@ func staticCssBootstrapMinCssBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/bootstrap.min.css.br", size: 17355, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/css/bootstrap.min.css.br", size: 17355, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -203,7 +203,7 @@ func staticCssBootstrapMinCssGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/bootstrap.min.css.gz", size: 21487, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/css/bootstrap.min.css.gz", size: 21487, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -386,7 +386,7 @@ func staticCssPlayerCss() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/player.css", size: 3355, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/css/player.css", size: 3355, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -403,7 +403,7 @@ func staticCssPlayerCssBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/player.css.br", size: 695, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/css/player.css.br", size: 695, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -420,7 +420,7 @@ func staticCssPlayerCssGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/player.css.gz", size: 815, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/css/player.css.gz", size: 815, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -444,6 +444,10 @@ body { padding-left: 65px; } +#detailChart { + display: none; +} + /* Custom page footer */ .footer { padding-top: 19px; @@ -451,6 +455,15 @@ body { border-top: 1px solid #e5e5e5; } +/* uPlot style customization (no title or legend) */ +.u-title { + display: none; +} + +.u-inline { + display: none; +} + /* Customize container */ @media (min-width: 768px) { .container { @@ -494,12 +507,12 @@ func staticCssStyleCss() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/style.css", size: 926, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/css/style.css", size: 1079, mode: os.FileMode(420), modTime: time.Unix(1613916153, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _staticCssStyleCssBr = []byte("\xa1\xe8\x1c\x00`\x1c\xc61\xe1U\x8c\xbao\n\x9c\xf3\v\r\x1fQ\xc0\a\x8b\xcaR\x00\xb9\xff\xcd%\x1e\x8d\x04!\x86\xb1g\xb3\xfd\xe6\xbes70$\fM\x15\xf1v\xc0M\xd6C\xa1@\x1f&5\xe3\xbe\xfd\xe5\x91L\xea\r\xb4\x00\xff\x9f\x97\x00\x16ef\xda7`\x94)\x0f#\u007f\x04\xbf\x8c\xe7\xa4k\x00AC\xf4?\xa1\x8c,1\x16VX\xfd\x11\x9b<9@_\xf8\xe4b\xc0\v\xb2\x167u\u01efGI\xcd\U00066807m\x9c\xb0\\|4I\xc2o\xf6!\x1dE\vr5\xec\xc3\xfcH\u05f7\xf6\x13\x8a\x94\u04f5J\xa0\xdb6\xe1xK\xe6\xf8L\v\xf8N\\\x80\xacd)\xac\u050c\x9a\xd0\xebZ\x83_C\x13\xe4Z\x86,.\xad\x9f\x8f8\xf1\x81\x19j\xf3\x99\xec\xebA\u06d0\r\u075c.j\r\\~|\x15\x16\xf7\x1a\xd5V\xe0\xdc\xfc~\xc5\xc0\r\xf1\x02c\xe5\xaf&\x94\xaa\xc0>\x89\x02\x9a@A\x91\xacp,F\x05\xfe\xdf\t\f*u\\i[\x17\xb2)\xc9$\xca\u007fg0\xd4Gn\xff\x85{o\x89\xeb\r[\x9c<\u047c\u0727\xeaZ\xcd\u0733\xe5\xe2l\x85\xab\xea2\n'8s\xac\xf9\x06+'\xe4\x15\xc0\x13o\xc3\x02V[\xe8\x03`T#\x95\xcdp\x80\x9a\x89\xe2\\\x036\v\xfbbG\xb0n\u02c2\x8b\x951A\xd41H\x00") +var _staticCssStyleCssBr = []byte("\xb1\xb0!\x00 &\xcd\xe9c\u0535\x03\xcew\bBO\xb8\x02\xd1\xf1\xf7\x0e,*\x94\xd8n7\u05b4hL\xfa\xf7\xda6\x80~`-\xe2y3P9\xd5\u017a\x9a4\x8a\xcbb)\x8f\xeb\x9a[`-\b\xeb\a=\xa1\xa6A\x91\xf6\x8d\x10Q\xf9\xc3\u007f\x9b#\x11gq\x8d\xfa\x82\xfaGN\u067d\"3*('\x14\xd8\x16\xc4&\xe8\xce\xfb8\xcb\xc5\x1a\v[\x93\xaa+\xce+{E\xf1\xa2\xa1gE\x9c\x80o\x18\x8c-\xfc\xf4\xa8\u04a5E\xbb\xe6\x1e\xd5\xfc\xc6\xe5\xa2\xed+2\xd2\xd4\xe5\\\x98\xf8\x8c\xba\xc6X\xf4`+\x18\x01\x1bb\xdd#\x87\x8ar\x8f\xdar-\x88\xbf\x86\xad\x9c\xdc\xdf5t3\xd7\x17\xfc)\x9a\xcdG\xfdW3\xcfWe\x97+{\xe9(\xe3\xe7H\xa9\xbb]\xf9\xda_Fq\xc5p\x1cg\x983}~%J\xa3\xdc\xfd\x85\u05f0\n\b\u00a9\x15\xf7W\xab\xd8v\xbb\xb9K\xc1>\xff+f\x96\xcb\u040d\x9aA\x1b\xd7\xcb-iV\xd4\xca\fo\xf1Rn@\x8b\xc78\x8b\x8d4\xd2}\xb6\x8b\xd4,\xbe\x06\x1f8\x88\xfc\xac=\x80\xa2\u0444i\x83R\x91\x87J\x9c\xdc\xeeo\x82\r\x9c\x13,\u06d7r]Q\xaf\xf57\x97\xb30s+\xf3\xb1x\xf0\xb3Lu\v\x14\xaa6\xaa\xf5{\x99\xa8\x1b\xcd\xff\xae\u045c\xc8bw\xbfx~\x17\xb5\x89\xdc,\xba\x02\xe16\x008E\u0136\x04\x88\x8d\xbf\xb8\u0431\xa3%3\xf7\x87]\u065c\x9f@\x92\x0f\xcdJ\xa4\x96\x17\xbc<[\x9f~-\x89;\xb7\x00") func staticCssStyleCssBrBytes() ([]byte, error) { return _staticCssStyleCssBr, nil @@ -511,12 +524,12 @@ func staticCssStyleCssBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/style.css.br", size: 343, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/css/style.css.br", size: 390, mode: os.FileMode(420), modTime: time.Unix(1613916656, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _staticCssStyleCssGz = []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x02\x03tSWr\xdc0\f\xfd\xdfS\xbc\xf4\xba\xc5\xdd\xe6\xa6\xe7\x00i'\xa0\x96\xb0\u0109DpH\xc8\xddw\x8f(j\xfbd\xab\x84y \xf0\x8a\xa6o\xf1\xc7\xeb\x05\x81[\xc1\x82\x9d\x90\x13h\x14V\xf0v:*\xd8\xdc\xe2~\x04xm\x8cu\xe5X\xd8+\x9c\xcf\xfc\xcd|\xa3X\xb0\b7\n\x87}\xfdq4\x9a\x04\xbe\xee\u06c0F\x87\u04baqM\x97\xa20\x9bo\u0582-\xabT\xcc-^\x974\xaeH\x1b\nCk\xa1\x17\u007f\xcb\xc0\xad3\nm\xa8_O&S\xdd\n\am,O\xe2U\xf9f\xbe\x03\x1b{\x8eV,;\x05a\x8f4s\x0f\x12\u0213\x16\x05\xc7\xc3\xe5\x1c\xbb\x90h\xefH\xe1\xa4#\x83\xd3\xd3\xcct\xcd5\x139=\x19\x98v\xf2}oc\xc7\x1ei\u007f\\2\v\x85\xa4\xdc$_\xee\x8bwp\x91\x8f\\p\xcdA\xe1\xd9\xd9\xd9Y\xba-8t\xcc\aH79rm\r\x9e\xd1Izo\x8f\xea\xd6\xeb\x9d\xd2\xd6\xe5Y_\x1a2V\xe3u\u04c9zm\x8dT\ng\xa7\xe7\xfe\xe6M?}\xb2\xc6.-\xb9Y\xc1\x8e\x06+\x1f\xbb\tk\xe0\xd8\xe9\x90,\xfc\x84*7e\xc7\x14\x12|iX\xd3A\xadP\x13\xb7\x10\xc7K\xc4&\xc0\xe3\x1d\xaa\xe3\r\\\xe6yx\xbeV\xf17E\xcf.\xda+R\xf8\xc9A\x82\xb6\x02\xd1EM\x12\xa1\x9dA\xeb7\xa8\xc6E r\xa9\xfe?\xd6\xfd\x91\r_\x11\xa4\xa2\xa5\x03\xb8&D\x12\x90\x0e\xb5\xcd\xda\x01\x93\x1c\xba\xf7#`\xbdr\xba[[\xb8\x1f\x80\xd9v*\xd6QNR\x02[OUZ\xa0\xd1Q\u049c\u007fC\xb1\x12%\x8b\xc0s\x11r\x94\x00\x00[Z\x12_\x9e\x03\x00\x00") +var _staticCssStyleCssGz = []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x02\x03}Sgz\xa50\f\xfc\xcf)\xb4\xbd\xbe\x92\x9e\xf0\xb6\xef\x01\xb6\x9d\xc0`\x05\xfc\xad\xb1\xfc\xd9\"=w\x8f\x8d\xe1\xf5\xde\u0108\u044cG\x93\xf7\xf0\u03ca\x12\x81Z\x86\x92\f\xa3a\x10P(\x86\xf7\x93\xac y\v\xf7\x19\x80\x15R*S\x8d\x98l\x0e\xe7S{3[*\x16\xc4LM\x0e\x87]\xfd1\xcb\u018e\xaec[x4\xc2U\u028c4^r\x0e\xd3\xd9r\u0369\xaaN\xc5\xd8bE\x85\xa3\x1a\x85D\u05f7\x16\xa2\xfc_9j\x8d\u0321u\xfa\xedx<\x11-\x93\x13R\xd1\xd8_U\xeffk\xb0\x91%\xafX\x91\u0241\xc9B\xe4\u07008\xb4(8\aC\xfd\xcf\x19\xacC\xbc\xba\xc3\x1cN\x82\x188=MJ\x17Z\x93\x90\u04d3^\xe9\v\x89,\x94\xfeY\v\xc7\xfd\xd8Ry\xab\xc5m\xa40\u0601\x82\xc7?[\xcf\xd4@\x14\t\x97D\x8c.\xda;N?7\x1d>\xb8H\xbc%ir9\xbc8;;\x8b\u007f\vr\x12]\x0f\t\xe3y\xd2J\xc2\v<\x89\u03c1\xaa\xfd\xad\x89\xc1\xf3\xadF(;Zu'\xa2+\xf0\xd6\x10\xb0\xe2P'\a\x1a+4\xf2]7F;J\xe5\xfbl\xdb\xf8\xe1\xb22Z\x99t}\x8f\xbc\xe0[\x8cP0\xc4$}\xdf\x1a\x94J\xc0\xdb&\x9c\xf6\xb5\x92\\\xe7pvzno\xdeuw\x1a/\xb0CVn\u6c23>c\x8f\x81a\x01\x1c\x19\xe1b\xb6\xbe@\x9d\x9aR\x94r\x88\xf0!IM\x80*\xc6\u01af \x8e\a\xc42\xc0\xc2\a\xa8\x8f\x97p\xc9\xdb\xc30\xe3 \xed/zK\u01ab+\xcc\xe179vB1\xb0(4\xb2\aa$\xb4vI\xaa/\x1d\xa2\x89\xf5]\xaa\xbb[6t\x85\xc05\x0e\xa7\x0e\xd7\b\x1e\x19P8\xad\x92w\xc1\x9f\xb4\r\x1f3\x80\xc5\xc8\xf1\xdf\"6\x9b\u025c\xae\xc6u\u0631d%\xc0\u02ba\xc7\x01\x1a\xe19\xf2\xacP\xae\xee\xee\xb0\xde\xcbG\xf2\x04\xe2>\xf7\x967\x04\x00\x00") func staticCssStyleCssGzBytes() ([]byte, error) { return _staticCssStyleCssGz, nil @@ -528,7 +541,7 @@ func staticCssStyleCssGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/style.css.gz", size: 437, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/css/style.css.gz", size: 504, mode: os.FileMode(420), modTime: time.Unix(1613916656, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -545,7 +558,7 @@ func staticCssUplotMinCss() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/uPlot.min.css", size: 1823, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/css/uPlot.min.css", size: 1823, mode: os.FileMode(420), modTime: time.Unix(1612375922, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -562,7 +575,7 @@ func staticCssUplotMinCssBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/uPlot.min.css.br", size: 597, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/css/uPlot.min.css.br", size: 597, mode: os.FileMode(420), modTime: time.Unix(1612375922, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -579,7 +592,7 @@ func staticCssUplotMinCssGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/uPlot.min.css.gz", size: 740, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/css/uPlot.min.css.gz", size: 740, mode: os.FileMode(420), modTime: time.Unix(1612375922, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -596,23 +609,35 @@ func staticFaviconIco() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/favicon.ico", size: 16958, mode: os.FileMode(420), modTime: time.Unix(1608210394, 0)} + info := bindataFileInfo{name: "static/favicon.ico", size: 16958, mode: os.FileMode(420), modTime: time.Unix(1587405572, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _staticJsAutoradioJs = []byte(`$(function() { - // Setup tooltips. - $('[data-toggle="tooltip"]').tooltip(); +var _staticJsAutoradioJs = []byte(`var chart = { + palette: [ + "#268bd2", + "#cb4b16", + "#d33682", + "#859900", + "#b58900", + ], - // Set up charts (via uPlot). - $('.chart').each(function(idx, value) { - let el = $(value); + setup: function(element) { + let el = $(element); let queryName = el.attr('chart-query'); let queryArg = el.attr('chart-arg'); + let queryRange = el.attr('chart-time-range'); let title = el.attr('chart-title'); + let url = '/timeseries_query?query=' + encodeURIComponent(queryName); + if (queryArg) { + url += '&arg=' + encodeURIComponent(queryArg); + } + if (queryRange) { + url += '&t=' + encodeURIComponent(queryRange); + } $.ajax({ - url: '/timeseries_query?query=' + encodeURIComponent(queryName) + '&arg=' + encodeURIComponent(queryArg), + url: url, dataType: 'json', success: function(data, status, xhr) { // Build the uPlot metadata. @@ -632,12 +657,44 @@ var _staticJsAutoradioJs = []byte(`$(function() { opts.series.push({ show: true, label: data.labels[i], - stroke: "blue", + stroke: chart.palette[i % chart.palette.length], }); } - let uplot = new uPlot(opts, data.data, value); + let uplot = new uPlot(opts, data.data, element); }, }); + } +}; + +$(function() { + // Setup tooltips. + $('[data-toggle="tooltip"]').tooltip(); + + // Set up charts (via uPlot). + $('.chart').each(function(idx, value) { + chart.setup(value); + }); + + // Add callbacks to the statistics links to create dynamic charts. + $('.stats-link').click(function() { + var el = $(this); + var name = el.attr('data-stream-name'); + var arg = el.attr('data-chart-arg'); + + var chartEl = document.createElement('div'); + chartEl.className = 'chart'; + chartEl.setAttribute('id', 'detailChartDiv'); + chartEl.setAttribute('chart-title', 'Listeners for ' + name); + chartEl.setAttribute('chart-query', 'stream_listeners'); + chartEl.setAttribute('chart-time-range', '6h'); + chartEl.setAttribute('chart-arg', arg); + + $('#detailChartTitle').text('Listeners for ' + name); + $('#detailChartDiv').replaceWith(chartEl); + $('#detailChart').show(); + chart.setup(chartEl); + + return true; }); }); `) @@ -652,12 +709,12 @@ func staticJsAutoradioJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/autoradio.js", size: 1367, mode: os.FileMode(420), modTime: time.Unix(1613515746, 0)} + info := bindataFileInfo{name: "static/js/autoradio.js", size: 2652, mode: os.FileMode(420), modTime: time.Unix(1613916592, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _staticJsAutoradioJsBr = []byte("\xb1\xb0*\x00\xe18(7\x9d9\xa4\x8d\xfd\x97\f\"\xd4mZ\x0f\xfd\xa4\xf5\xa9Eld\x98\x83X0\rb\xa9`\u05d7lPr\u007f\xdcO`~\xf5\u06d6\r4\x1bE\xa6\xcfv\xc3L\x1d\xe2\xdc]\x916\xb0\u0734\xb2\xec\"\x19@\x1c?f\\r\xd7v\x8b\x8e\x89\x95\x95\x98\xac\xe7\xbf*\x8c\xfaX\x8e\xc4Pt\xb4w]\xe9\x063:\x03J\x9a\xd77T\x9b)\xf7\x9c\x9c\xb6hFG\x83\x01,pZQ\xfc\x9b\xc5\xebE\xaf\xb8P\x17\x01\xf6\xd6a\xd1\xea\xf0\u0455\x8d>\x05\x9b\u06a5[-\xach\xea\xdcU\x89\v3\xec\xd4\x14\x95\x19\xbdW\xfd\xa9\xd3\xebjHh\xe6\x18\xd31\x19\v\xa3\xfc\xbdn\xa4\x95\xfb\x1f\xc0\xfd}\x1d\x89\xdd\xe6`#e\x8e\u075b\x9d\u5996\xe0\x13\x18\xb5F\xfb\xd5\xf8Huz]\tS]:u\xe0\x18\x83\x859\xe8\xad#\xdcQE\x04$\x05\xd2[\v|D(\xbf(\xe4\xc4k}\x85?\x0e\x10Xz\u053a\u07edm<M\x1e\u0683\ua551\xbc`\x87\x17\xa1j\xb5\x8c\xf9\x01`\xad\xfa\xae\u0681\x90\x9cy@\xda\xc9=\x14>\x98\x874\x1b0\xe6\xb3\xeb\x98\xe5\x0e\x00\xbe+\xd7\x18\x1e\xd3\xf3\xdf\xc0\xc4\xd1?\x9ecU)w\x8b\x1b\xd3-o\x10Bh1;>D\xf0k\xaa\xe9\xf5\x82\t!\x89\xdb!\x93\fO&\x98Qs\x962S%\xc9l\x86\xc4-\xee\xaf\xd1({\x91J@\f\xd3\xf0\xad\x8fO\x8f\xcb\x10\xcb9\xc7\xfc`F\xba\x91\x93\xc8\xfc\xc4\xc8wE\xbd\x1cs\x9a\xcdP#\xf5\u6ba7\x9d\xadS|^\xc5<D\a\xfek4\xcc=S\x05\xf6'G\xe346\u0503\xffU\x90\x15J\xca\x02/eO\xa6;\u0689[") +var _staticJsAutoradioJsBr = []byte("\xc1\xd8R\x00\xe1,\xa8n\xac\xe6\x96\xf1\u039c\xbdb\xf4/\x99\x89\xb25\xad\x87\xa6ETv\xb6b5L\x03\xbc6\x88\x82]\xdf\xdcIQ(69\xfd~\u0344\xb15v\x85k\x93\x97\x02\x1e\xa3P\xdb\xf1\x9f\x0e\x10\x1c\x90\xb0(\x8c\xbd\x0fi}\xa78c\xc3W\xa0\xb4\xf4\xeb\xff\xb3\xe97\x00n\xfa\xe4\x99}\x8d\n\x00\x00\b\x86r\xa5\x8aq\xb9`\xe2\xdcLk\n&[\xcat\xf9|\xa9\x92\u02ec\x14\xab\xd5L\xa6\xc0\x14+\xd5\xc51\x8eYM]3\xa9\x9c\xd3\x12\xbf\xc4\u007f\x9c<\xc3CS\x18am}D\x83\xb3=}\xd5\xe4\xa54\xf3CP\xf9\x1a\xc9\u0546DrA\xf7vt\xb1~l'\xbc6&\u0707\"\x1bIK\x05\ti\x87\u021a\x90 z\xdfk\xf9\xfa(\xcd\xd0l\xdaXu\xb7\xd1>\xf2T\x93\x10\x87\t+\xe5v\x87h\x1c\xe5\xf2yJ\x1c\a\u0421Z|\xfc\u0628~l\u0652\xdc]l\xf0\u03df\xfaH[o\x9e\a$\x8f\x0f\xea\xe1\xef\xc6#\u0338^;\\\x9b\x00\xa0\x16\xa3\x9d\x8d\u0550\u007f\\\x87\x10T\xcd\xddj\x05\x92f\xe2{\xaf8\xf7k\xf7\x90?\a\x00\xe9\xf4#\xf3\x03\xef]\x02Wx\xf2\xe1K _7\x85O\xcb!O\u0728\xc9\f\x03\u06baY;\xb6q\x02I\t\x12\\k|\"\x9fw\x19\xd9\xf2\xd8j\x8bZltp\t\xfc\x9ex\x83\x05\xe4\xc0\u007f:\x19\x9c^\xb2\xf2JX\xf9\xc4\x13\xba\u01db\xa0\x90\xeb\x0f\xeb\x87A\xac.\\\x8c\x93}Q\x938\x9cV\x99E(\xb4\xa8\x11\x95\xd2'\u0269\xfd\x02\xe9f|\x8bl\xb7\xe9_\x02z\xd1-?Gx\n`S\xb1\xb2\xe345\u00e5*\rHCc\xb7\xd7\xd4\u4e24\xda/(\xf2O~\\\x8f\xbe\xf6\xad\x1b\xc93\x17\xb7\xfb r\xa6\u0292\xc2\xeb\xc8\xff\xab\x8b\x1e\b\xc1]\xb1\x8cU\xbf.\x9e\x01\xa1b\x85\xfd\x13</\xfb\xaa`^\a?\xfe\xef*\xfc\xf0\x15\xd5'\xde \xa1\xbcH5g>,HeT\xf67\xb8\x11\xc6bpA\xf2\x1f\x14\x82,\xae\xc2\x06\"\x9b*\xba\x9e\x10\xdb\xebmGrl\xa4\xc0%\x06\b\x86\x8e(\x13{\xf7\xf5\x82\x8a\x15{\x1a+~Q*\x06WI\u072eNu\x9d\xbbQ\xfe\xd6\x13|}C\xaf\xbcx^\xb9\u0229\xb8+1\x1d\u0458\xad\xf1\u035c\xad+#\xb1'\x16*\xaf\x05<%\xcf>p\xbdK\xa0\xa3\xfe,\x91\xa8'\xc1\xeb\x11\xc9\x1a=\xd4\u008fv\xfe\x82\xc0\xc3\f%=\x1f\xb0 \xb7\u007f\x0fDSy\x8f\"\x13h\xea\xafOx\x81T\xa4\x9b\xf6\x8e\x12(\xf3\xa9\xf7g\x1b\x0f\xb8\xfb\x84~$\x05\a{\xda\xc8q\xffx6\xfcqB\\G\xcb\xc1\x881\t}S\xe1\xd0\xea\xc4o\x99$\xa9d\x00/O\xa0\u048eT\u06af'\xac\xe3\x99\x00\x89\xa9\xab\x90\xe6,Y\xc0W\xb7\xb2\u0122\x8a\x81\xaf\xce\xca\xc3_\xf1\xbd\x11j\x9eH5D2\x91\x8f\xb7\x10\x15\xc9\x13\xdaKc+-\a\a") func staticJsAutoradioJsBrBytes() ([]byte, error) { return _staticJsAutoradioJsBr, nil @@ -669,12 +726,12 @@ func staticJsAutoradioJsBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/autoradio.js.br", size: 439, mode: os.FileMode(420), modTime: time.Unix(1613515906, 0)} + info := bindataFileInfo{name: "static/js/autoradio.js.br", size: 755, mode: os.FileMode(420), modTime: time.Unix(1613916656, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _staticJsAutoradioJsGz = []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x02\x03\x8dT\xd7v\x9c0\x10}\xf7WL*p\x96\x96\x9e\xb0q\u007fI\xefyI\x97a\fr\x04\"\xd2\xc8\xdd\xff\x1e!|\xb6b\x9f\xbd\xdb\xf7\xdeiwtt\xdb\xdf7MN\\6~\x00gk`\x91$\xf0\x19\u0274@R\n\u2b4e\xdd\u07f7}\xef{\xc1\x88E$\xcbR\xe0\xfa\xcdK\xfa\xe6O/\x88/\xbf\xfb\xc1xm&\a\x98\x16\xf2\x8a)\xd2\xe0\x1fr\x06\u60d0\x14L\xd2\u014e\xb3\xd1\xc8\xf2j\xda\a/\x8eC8d\xc2`\xdfQ\x0f\x81\x04(`\xdd\x06\xf6\xdcx\x8e\xfagP\x9d\xbcc5Z\x05\x8a\x98\x11)\xdfs\xf9#GyC\xfamU.\u02d9*\x17\xc5\xc4I\f$v\u007f\xcfjo\xc7\xec\x80\x1d\xfb\x93\xa6\x1d\x8c\x12\x19x\t\xf1\x1a5*\x8e\xfa\xb7\xab\xbd\xe9\xde\xd7=\x18\x016\xb9,\xf0\ub9d7\xbb\xb2ne\x83\r\xf9\x93q\x02\xcb{wmO\xd7(\xdd A8W\xb5\xdb\u0517\x93\x16m\xe9\x03-\x1bo\x9e\xd5&\xcfQ\xeb\f&\xa6w\xfa\x10412:\x84\xe3JM\xbc\x9f\xc0mu\xc7pQ\x00U\xd8/\x13j$\xd6\xc5\xc6K\xe2\xce8\u0652\x86u\x97i\x19\u03be\xac\xff\b\a\x15G\xbc\xa0*\x83\x87i:\xccW\xc8\u02ca2\xb8\xf7\xe4\nAox\x06\u07c7X\x87\xb3\x8b\xe1\u021f\xcb\u007f_\x8c\x87\fyiWR\xf3\xa6\x04\xb7`bu\xab\x81)\x84\x9a\v\xc15\xe6\xb2)\xf4\xb29\xfbR\x81=\xc7\n8\xacC:\xb6\x1f\xcf\xc1\xd9\u063d}O\u007f\xc6\x02\x9b\x92*K\x8cF\xc1\xb0\u007fs\xfa\xef\xfc'$\xebp/M\xd3\xf1r\u3ad7\x17l\x0f\x85^\xa1\xba[m\xdc\xfb\x1b\xb7FW\xfe\xa0\xccAW\xf2(\x03Rfx\xcd\x0e\xaep6\u06c4\x1d\xe9j\xb9&%\xffb\x067\xf7\x84\xc1\x9b\u00fa\x8b`\x15+\x84\xbb\xa6\xba\xa3\xbc\x0e\r\x1e\xf5\xc7\xda\xef\xa6\v\xa7\x0e\x87\xb0p\xe78L\x8e\u03a4\x96\xfb\xec^\xff\x0131\x00rW\x05\x00\x00") +var _staticJsAutoradioJsGz = []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x02\x03\x8dU\xe7v\xa4<\f\xfd\x9f\xa7\xd0V\xc8\tCz#_\xef\xbd\xf7\xee\x01\x05\xbc1\xe5\xb3Ez\xde}-\xc3\x0e\x10\xe0\x9cq\xeaXWW\u04b5,_\n\rq&4\xc1\xdbp\xb7\x01vUB!\x11F\xf0\xe7\x06\xb4\xeb\u9cfd\xa3\x93e\xb2\xf74\xe8m\xc5\u02c3\xe5\xee\xd1`+\xd9\xdf?:\x19\xa2N\x0eOOwv\x06[\xcb\u00d3n\xeb\xef`\xc3\xfd5Hu\x15\xc1y]\xc4$\xcb\xc2G\x859\x16\xb4\xc9I\xb5K!\x01*\x9b\xe7\xf3\x95\xf5l`\xfc\xbfF}\xf3\x8d\xc8\xd1bP\x85\x82H\xfb\x9e+n\xe1L\xde\x14\xfe}\x9d\x8e\xe1B\xa7\x93\xe0\x1fD\x91N\xb0\x93\xccq\xa1\xd9\xf6\u060b$\xa9I\aR#l\xad\xb98o\x9b\xd9\fj\x89\xe6_\x17\xf4]\xf7\xfbm\x0f\xb6\x00\x8b\xb8L\xf0\xe7\x1f>\xff\xb0\u032b\xb2\xc0\x82\xfcU\xd1=6y\x0e\xfe\x9b\xe2\xfa\n\xf2\xe2([6\xccK[\xe3<g\xeb\xdbQ>\x8c\u025d\x18\xb3\xf44G\xde\xf9N\xd2?\x0f\xc5+q\xed\x8fX#\xfe\x15\fv\x13A\u29db\n#\xf0^\x99\xb2\xf0\x86VS\xc71\x1a\xd3\xeb)\xc6\a`HPm\x02\xb8\xcet\x93\xfcpmo\xc3\a\xb5T\tP\x86P\u007f\xa7J\x82\x1cI\xb0o8\x02\xf3\xb1\x95\x15\x99\xf6\xf2\x8c\x97;\xe8\xa8\xf9\x13L\"\xaedBY\x04\a;;\xd3\xf6\fe\x9aQ\x04\xbb\xc73\x80\xa6U\xdcu\x9dYw\x0f\u04de\u007f\x8f\xb7\x1f\u03a6\x04\xf9\u071eb.\x8b\x14\\k\x92\xc8+\x03B#\xe4R)i0.\x8b\u010c\xc59/5\xf8<_\xa4\x95g\xe7\xcc\xfey\v\x9c\x8c\xfc\xeb\u03dd\xbfC\x85EJ\x995lmm\xce\xe8\xd7\xc7\xff)\xff\x86\xed\xb7awggg\x9c\xe5\xc3\xfa\xe1\x95X\xa22\xebD\xe7\xa3\r\x1b}\u00ea6\x19w\xe5\xcc2Yy\x15\x01\xe9\x1a\x83Y\x8c\v\x1c\xf5\x93\xb0%\xcd\xc3\r\xe9\xf2\x02\xa3f<\x87\xedX\xfeS\u008b\xe1N[\xc8\f\xd1\xc3\xe6:Z\xb9\xf1Sq\xaf\xbf\r\x05^5}\xefs\xf9Aw\x04\x01\x8c\xc7./\xee\xaeA4\x8e`\x1bi\u3e7f\xbaz\xacp\xdbL?\xf2\xa4\a*KE\xb2j\xdb\xe6\xb9\xef\xfd\xc9!\x16T\xa6\xa9\u00b7\x9f\xb6\xe6\xa7\u007f{\x9ba\xfb\xbfo\xb9;\x0e\u03b7\xd1\xc1\xd8S\x96\xa2IysE\x17:\x9b\xf5F\x11g]\x1e2\xb9\x0e\xe0R\xa8z0\xb8\x1a=\r'\xe67\u01b6\x8c^\xc8\xf7\x93\x04b\xa1\xd4R\xc4\x17\x06\xa8t\xf3\x81g\x894$c\x03J\x16\xcd~\xacQ\x10BrS\x88\\\xc6m\x8e]^\xecb\x16\x8c\xb6\xc9\xc5J\xc6\x17c\x95xq\u7daf\x1de\xd2pF=K\xf1\xe8\x95s\xe2\x19\xb2\xa1\xf3\x05\u06fcGx\xa1\xd3\x11|\xf0\xd4\r\xd0\xce\xf21\aO\u02b8\xe6#\x0f\x9b\xaa>n\x1a\xc0\x12\xc8\xcb~\x88\xd6\xc1\xd6#\x8ci_`\xaf9\x811\xc8 \xbdO\xa4\xe5\xb2&\xf4=\x99x\x01x\t\x92\x90\xeaCF|4\xa2\x1ey\r\x9eQv\xffJ\x1a\xc2\x02\xb5q\xb7\x9e\x1f\x1e\x16aM\x16\xf7 1K\xa3\u07ff\xea\r\u067ait\xcf?\xb3\x1ce\xeb\xfa\xb1\xf4\x01\x1fMO\u007f\xee\x91g=5~\xe2\x12\xf9\x12\xe05\xf9\xf3u\xcex;-C\x8d\x95\x121\xfe*)\xf3\xdb|\xe6],\x9eg\x99\u03c8\xf1\xf5\xe8\xf9\xaf\xcc\xda\x1at\xe1f_wm\xf8\xe75}\xb4\x95\xcb\\\n\x00\x00") func staticJsAutoradioJsGzBytes() ([]byte, error) { return _staticJsAutoradioJsGz, nil @@ -686,7 +743,7 @@ func staticJsAutoradioJsGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/autoradio.js.gz", size: 520, mode: os.FileMode(420), modTime: time.Unix(1613515915, 0)} + info := bindataFileInfo{name: "static/js/autoradio.js.gz", size: 893, mode: os.FileMode(420), modTime: time.Unix(1613916656, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -709,7 +766,7 @@ func staticJsBootstrapBundleMinJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js", size: 80698, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js", size: 80698, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -726,7 +783,7 @@ func staticJsBootstrapBundleMinJsBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js.br", size: 20027, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js.br", size: 20027, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -743,7 +800,7 @@ func staticJsBootstrapBundleMinJsGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js.gz", size: 21959, mode: os.FileMode(420), modTime: time.Unix(1582194631, 0)} + info := bindataFileInfo{name: "static/js/bootstrap.bundle.min.js.gz", size: 21959, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -762,7 +819,7 @@ func staticJsJquery351MinJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/jquery-3.5.1.min.js", size: 89476, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/js/jquery-3.5.1.min.js", size: 89476, mode: os.FileMode(420), modTime: time.Unix(1588633359, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -779,7 +836,7 @@ func staticJsJquery351MinJsBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/jquery-3.5.1.min.js.br", size: 28007, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/js/jquery-3.5.1.min.js.br", size: 28007, mode: os.FileMode(420), modTime: time.Unix(1612429991, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -796,7 +853,7 @@ func staticJsJquery351MinJsGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/jquery-3.5.1.min.js.gz", size: 29838, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/js/jquery-3.5.1.min.js.gz", size: 29838, mode: os.FileMode(420), modTime: time.Unix(1612429991, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -852,7 +909,7 @@ func staticJsPlayerJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/player.js", size: 1240, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/js/player.js", size: 1240, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -869,7 +926,7 @@ func staticJsPlayerJsBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/player.js.br", size: 370, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/js/player.js.br", size: 370, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -886,7 +943,7 @@ func staticJsPlayerJsGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/player.js.gz", size: 502, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/js/player.js.gz", size: 502, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -905,7 +962,7 @@ func staticJsUplotIifeMinJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/uPlot.iife.min.js", size: 35191, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/js/uPlot.iife.min.js", size: 35191, mode: os.FileMode(420), modTime: time.Unix(1612375922, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -922,7 +979,7 @@ func staticJsUplotIifeMinJsBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/uPlot.iife.min.js.br", size: 14165, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/js/uPlot.iife.min.js.br", size: 14165, mode: os.FileMode(420), modTime: time.Unix(1612375922, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -939,7 +996,7 @@ func staticJsUplotIifeMinJsGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/uPlot.iife.min.js.gz", size: 15215, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/js/uPlot.iife.min.js.gz", size: 15215, mode: os.FileMode(420), modTime: time.Unix(1612375922, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -956,7 +1013,7 @@ func staticRadio52Png() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/radio52.png", size: 1051, mode: os.FileMode(420), modTime: time.Unix(1577624368, 0)} + info := bindataFileInfo{name: "static/radio52.png", size: 1051, mode: os.FileMode(420), modTime: time.Unix(1581689120, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -980,7 +1037,7 @@ func staticSpeakerSvg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/speaker.svg", size: 524, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/speaker.svg", size: 524, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -997,7 +1054,7 @@ func staticSpeakerSvgBr() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/speaker.svg.br", size: 278, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/speaker.svg.br", size: 278, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1014,7 +1071,7 @@ func staticSpeakerSvgGz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/speaker.svg.gz", size: 329, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "static/speaker.svg.gz", size: 329, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1056,8 +1113,11 @@ var _templatesIndexHtml = []byte(`<!DOCTYPE html> data-toggle="tooltip" data-delay="300" title="{{$m.IcecastMount.GetDescription}}" {{end}} >{{$m.Mount.Path}}</a> - <a href="/{{$m.Mount.Path}}.m3u">(m3u)</a> - <span class="badge badge-secondary">{{$m.Listeners}}</span> + <a href="{{$m.Mount.Path}}.m3u">(m3u)</a> + <a href="#" class="stats-link" + data-stream-name="{{$m.Mount.Path}}" + data-chart-arg="{{$m.Mount.Path}}{{range $tm := $m.TransMounts}}|{{$tm.Mount.Path}}{{end}}" + ><span class="badge badge-secondary">{{$m.Listeners}}</span></a> {{if $m.TransMounts}} <ul> {{range $tm := $m.TransMounts}} @@ -1065,8 +1125,11 @@ var _templatesIndexHtml = []byte(`<!DOCTYPE html> <a href="/player{{$tm.Mount.Path}}" data-toggle="tooltip" data-delay="300" title="{{$tm.Mount.TranscodeParams.String}}" >{{$tm.Mount.Path}}</a> - <a href="/{{$tm.Mount.Path}}.m3u">(m3u)</a> - <span class="badge badge-secondary">{{$tm.Listeners}}</span> + <a href="{{$tm.Mount.Path}}.m3u">(m3u)</a> + <a href="#" class="stats-link" + data-stream-name="{{$tm.Mount.Path}}" + data-chart-arg="{{$tm.Mount.Path}}" + ><span class="badge badge-secondary">{{$tm.Listeners}}</span></a> </li> {{end}} </ul> @@ -1087,9 +1150,18 @@ var _templatesIndexHtml = []byte(`<!DOCTYPE html> {{end}} </ul> + <h4>Statistics</h4> + <p><b>Listeners</b></p> <div class="chart" chart-query="all_listeners" - chart-title="Listeners"></div> + chart-title="Listeners" + chart-time-range="6h"></div> + + <div id="detailChart"> + <p><b><span id="detailChartTitle"></span></b></p> + <div id="detailChartDiv"></div> + </div> + </div> </div> @@ -1128,7 +1200,7 @@ func templatesIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "templates/index.html", size: 3201, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "templates/index.html", size: 3847, mode: os.FileMode(420), modTime: time.Unix(1613916140, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1174,7 +1246,7 @@ func templatesPlayerHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "templates/player.html", size: 955, mode: os.FileMode(420), modTime: time.Unix(1613515529, 0)} + info := bindataFileInfo{name: "templates/player.html", size: 955, mode: os.FileMode(420), modTime: time.Unix(1612357023, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/node/metrics.go b/node/metrics.go index a19f99779cb340482e9fd61365a47777524ed7da..d0bc92d32bac5e11f1a15c83e5e71d60b47643b0 100644 --- a/node/metrics.go +++ b/node/metrics.go @@ -21,7 +21,7 @@ var ( prometheusTimeout = flag.Duration("prometheus-timeout", 30*time.Second, "Timeout for Prometheus requests") knownQueries = map[string]string{ - "stream_listeners": "sum(stream_listeners{stream=\"$arg\"})", + "stream_listeners": "sum(stream_listeners{stream=~\"$arg\"}) by (stream)", "all_listeners": "global:stream_listeners:sum", } ) diff --git a/node/static/css/style.css b/node/static/css/style.css index ccbe95de9dff2bf4a09da2feec8fd49b48c264d5..4dbfeb338007968f5ee9c5ab6eacc25003eb183e 100644 --- a/node/static/css/style.css +++ b/node/static/css/style.css @@ -17,6 +17,10 @@ body { padding-left: 65px; } +#detailChart { + display: none; +} + /* Custom page footer */ .footer { padding-top: 19px; @@ -24,6 +28,15 @@ body { border-top: 1px solid #e5e5e5; } +/* uPlot style customization (no title or legend) */ +.u-title { + display: none; +} + +.u-inline { + display: none; +} + /* Customize container */ @media (min-width: 768px) { .container { diff --git a/node/static/css/style.css.br b/node/static/css/style.css.br index 987f30ffd8f5bcf4aa41d569f6dccc5c075ee35c..e051744eec7abe2cf246fd59b5db73c40213707a 100644 Binary files a/node/static/css/style.css.br and b/node/static/css/style.css.br differ diff --git a/node/static/css/style.css.gz b/node/static/css/style.css.gz index 1ade237cdf257d59480024576df433693fe9160c..792d1d9b9dee3791f0c6b4b289afa89b17777dd3 100644 Binary files a/node/static/css/style.css.gz and b/node/static/css/style.css.gz differ diff --git a/node/static/js/autoradio.js b/node/static/js/autoradio.js index 569ac73c5300fa40d6edc3f68582b514974753d7..4f8307327904412a6b34ee6fc061badcea7a6037 100644 --- a/node/static/js/autoradio.js +++ b/node/static/js/autoradio.js @@ -1,15 +1,27 @@ -$(function() { - // Setup tooltips. - $('[data-toggle="tooltip"]').tooltip(); +var chart = { + palette: [ + "#268bd2", + "#cb4b16", + "#d33682", + "#859900", + "#b58900", + ], - // Set up charts (via uPlot). - $('.chart').each(function(idx, value) { - let el = $(value); + setup: function(element) { + let el = $(element); let queryName = el.attr('chart-query'); let queryArg = el.attr('chart-arg'); + let queryRange = el.attr('chart-time-range'); let title = el.attr('chart-title'); + let url = '/timeseries_query?query=' + encodeURIComponent(queryName); + if (queryArg) { + url += '&arg=' + encodeURIComponent(queryArg); + } + if (queryRange) { + url += '&t=' + encodeURIComponent(queryRange); + } $.ajax({ - url: '/timeseries_query?query=' + encodeURIComponent(queryName) + '&arg=' + encodeURIComponent(queryArg), + url: url, dataType: 'json', success: function(data, status, xhr) { // Build the uPlot metadata. @@ -29,11 +41,43 @@ $(function() { opts.series.push({ show: true, label: data.labels[i], - stroke: "blue", + stroke: chart.palette[i % chart.palette.length], }); } - let uplot = new uPlot(opts, data.data, value); + let uplot = new uPlot(opts, data.data, element); }, }); + } +}; + +$(function() { + // Setup tooltips. + $('[data-toggle="tooltip"]').tooltip(); + + // Set up charts (via uPlot). + $('.chart').each(function(idx, value) { + chart.setup(value); + }); + + // Add callbacks to the statistics links to create dynamic charts. + $('.stats-link').click(function() { + var el = $(this); + var name = el.attr('data-stream-name'); + var arg = el.attr('data-chart-arg'); + + var chartEl = document.createElement('div'); + chartEl.className = 'chart'; + chartEl.setAttribute('id', 'detailChartDiv'); + chartEl.setAttribute('chart-title', 'Listeners for ' + name); + chartEl.setAttribute('chart-query', 'stream_listeners'); + chartEl.setAttribute('chart-time-range', '6h'); + chartEl.setAttribute('chart-arg', arg); + + $('#detailChartTitle').text('Listeners for ' + name); + $('#detailChartDiv').replaceWith(chartEl); + $('#detailChart').show(); + chart.setup(chartEl); + + return true; }); }); diff --git a/node/static/js/autoradio.js.br b/node/static/js/autoradio.js.br index 78aff6e4f5db4702c809db1bfa17f3093b19afd0..55916d23b5bb3de1c91120f3f7a2bda6da4d964d 100644 Binary files a/node/static/js/autoradio.js.br and b/node/static/js/autoradio.js.br differ diff --git a/node/static/js/autoradio.js.gz b/node/static/js/autoradio.js.gz index 2f15aae0aa81740ba492b434e267e1e68afeef89..d79a3537be367d2f483b03e87dccefa9aa463e36 100644 Binary files a/node/static/js/autoradio.js.gz and b/node/static/js/autoradio.js.gz differ diff --git a/node/templates/index.html b/node/templates/index.html index a5b23ed47aadf6f389fcce89e314a37f13a3a499..fb074115a0bd6bc01700ef1322c5bfeb036eb011 100644 --- a/node/templates/index.html +++ b/node/templates/index.html @@ -35,8 +35,11 @@ data-toggle="tooltip" data-delay="300" title="{{$m.IcecastMount.GetDescription}}" {{end}} >{{$m.Mount.Path}}</a> - <a href="/{{$m.Mount.Path}}.m3u">(m3u)</a> - <span class="badge badge-secondary">{{$m.Listeners}}</span> + <a href="{{$m.Mount.Path}}.m3u">(m3u)</a> + <a href="#" class="stats-link" + data-stream-name="{{$m.Mount.Path}}" + data-chart-arg="{{$m.Mount.Path}}{{range $tm := $m.TransMounts}}|{{$tm.Mount.Path}}{{end}}" + ><span class="badge badge-secondary">{{$m.Listeners}}</span></a> {{if $m.TransMounts}} <ul> {{range $tm := $m.TransMounts}} @@ -44,8 +47,11 @@ <a href="/player{{$tm.Mount.Path}}" data-toggle="tooltip" data-delay="300" title="{{$tm.Mount.TranscodeParams.String}}" >{{$tm.Mount.Path}}</a> - <a href="/{{$tm.Mount.Path}}.m3u">(m3u)</a> - <span class="badge badge-secondary">{{$tm.Listeners}}</span> + <a href="{{$tm.Mount.Path}}.m3u">(m3u)</a> + <a href="#" class="stats-link" + data-stream-name="{{$tm.Mount.Path}}" + data-chart-arg="{{$tm.Mount.Path}}" + ><span class="badge badge-secondary">{{$tm.Listeners}}</span></a> </li> {{end}} </ul> @@ -66,9 +72,18 @@ {{end}} </ul> + <h4>Statistics</h4> + <p><b>Listeners</b></p> <div class="chart" chart-query="all_listeners" - chart-title="Listeners"></div> + chart-title="Listeners" + chart-time-range="6h"></div> + + <div id="detailChart"> + <p><b><span id="detailChartTitle"></span></b></p> + <div id="detailChartDiv"></div> + </div> + </div> </div>