Compare commits

..

1 Commits

Author SHA1 Message Date
langhuihui
966f9b9d0d 修复录制功能 2020-01-30 11:51:42 +08:00
35 changed files with 485 additions and 547 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.exe
.vscode
.idea
resource

View File

@@ -13,6 +13,6 @@ ListenAddr = ":81"
#[Plugins.Auth]
#Key="www.monibuca.com"
[Plugins.RecordFlv]
Path="./resouce"
Path="./resource"
[Plugins.QoS]
Suffix = ["high","medium","low"]

View File

@@ -1 +1 @@
#app,body,html{height:100%}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#184c18;position:relative}#app>div:first-child{position:absolute;top:10px;left:30px;font-size:x-large}.content{padding-top:60px}.feature-title[data-v-54efad41]{color:#eb5e46;font-weight:700;font-size:larger}p[data-v-54efad41]{margin:30px;font-size:20px}img[data-v-54efad41]{margin:20px}.root[data-v-e34eab40]{background:#d3d3d3}.root>img[data-v-e34eab40]{width:300px;margin:30px}@-webkit-keyframes recording-data-v-c2fdc660{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}@keyframes recording-data-v-c2fdc660{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}.recording[data-v-c2fdc660]{-webkit-animation:recording-data-v-c2fdc660 1s infinite;animation:recording-data-v-c2fdc660 1s infinite}.layout[data-v-c2fdc660]{padding-bottom:30px;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.room[data-v-c2fdc660]{width:250px;margin:10px;text-align:left}.empty[data-v-c2fdc660]{color:#eb5e46;width:100%;min-height:500px;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.empty[data-v-c2fdc660],.status[data-v-c2fdc660]{display:-webkit-box;display:-ms-flexbox;display:flex}.status[data-v-c2fdc660]{position:fixed;left:5px;bottom:10px}.status>div[data-v-c2fdc660]{margin:0 5px}
#app,body,html{height:100%}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#184c18;position:relative}#app>div:first-child{position:absolute;top:10px;left:30px;font-size:x-large}.content{padding-top:60px}.feature-title[data-v-54efad41]{color:#eb5e46;font-weight:700;font-size:larger}p[data-v-54efad41]{margin:30px;font-size:20px}img[data-v-54efad41]{margin:20px}.root[data-v-e34eab40]{background:#d3d3d3}.root>img[data-v-e34eab40]{width:300px;margin:30px}@-webkit-keyframes recording-data-v-65ce2c22{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}@keyframes recording-data-v-65ce2c22{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}.recording[data-v-65ce2c22]{-webkit-animation:recording-data-v-65ce2c22 1s infinite;animation:recording-data-v-65ce2c22 1s infinite}.layout[data-v-65ce2c22]{padding-bottom:30px;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.room[data-v-65ce2c22]{width:250px;margin:10px;text-align:left}.empty[data-v-65ce2c22]{color:#eb5e46;width:100%;min-height:500px;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.empty[data-v-65ce2c22],.status[data-v-65ce2c22]{display:-webkit-box;display:-ms-flexbox;display:flex}.status[data-v-65ce2c22]{position:fixed;left:5px;bottom:10px}.status>div[data-v-65ce2c22]{margin:0 5px}

View File

@@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>Monibuca</title><script src=jessibuca/ajax.js></script><script src=jessibuca/renderer.js></script><link href=/css/app.e2fa93b0.css rel=preload as=style><link href=/css/chunk-vendors.22ebf426.css rel=preload as=style><link href=/js/app.d106a496.js rel=preload as=script><link href=/js/chunk-vendors.ae8ac63d.js rel=preload as=script><link href=/css/chunk-vendors.22ebf426.css rel=stylesheet><link href=/css/app.e2fa93b0.css rel=stylesheet></head><body><noscript><strong>We're sorry but dashboard doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.ae8ac63d.js></script><script src=/js/app.d106a496.js></script></body></html>
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>Monibuca</title><script src=jessibuca/ajax.js></script><script src=jessibuca/renderer.js></script><link href=/css/app.b2f5eee5.css rel=preload as=style><link href=/css/chunk-vendors.22ebf426.css rel=preload as=style><link href=/js/app.d4cf578d.js rel=preload as=script><link href=/js/chunk-vendors.ae8ac63d.js rel=preload as=script><link href=/css/chunk-vendors.22ebf426.css rel=stylesheet><link href=/css/app.b2f5eee5.css rel=stylesheet></head><body><noscript><strong>We're sorry but dashboard doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.ae8ac63d.js></script><script src=/js/app.d4cf578d.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dashboard/dist/js/app.d4cf578d.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dashboard/dist/js/app.d4cf578d.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Monibuca</title>
<meta name="description" content="">
<link rel="preload" href="/docs/assets/css/styles.92893aed.css" as="style"><link rel="preload" href="/docs/assets/js/app.92893aed.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.685ed1cc.js"><link rel="prefetch" href="/docs/assets/js/2.a6d3efaf.js"><link rel="prefetch" href="/docs/assets/js/3.a9fbea98.js"><link rel="prefetch" href="/docs/assets/js/4.727e40e9.js"><link rel="prefetch" href="/docs/assets/js/5.78b155e8.js"><link rel="prefetch" href="/docs/assets/js/6.35a311c9.js"><link rel="prefetch" href="/docs/assets/js/7.ab3a52c1.js">
<link rel="stylesheet" href="/docs/assets/css/styles.92893aed.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><div class="content"><h1>404</h1><blockquote>How did we get here?</blockquote><a href="/docs/" class="router-link-active">Take me home.</a></div></div></div>
<script src="/docs/assets/js/app.92893aed.js" defer></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="12" height="13"><g stroke-width="2" stroke="#aaa" fill="none"><path d="M11.29 11.71l-4-4"/><circle cx="5" cy="5" r="4"/></g></svg>

Before

Width:  |  Height:  |  Size: 216 B

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[1],{168:function(t,e,n){"use strict";n.r(e);var a=n(0),s=Object(a.a)({},(function(){var t=this.$createElement;this._self._c;return this._m(0)}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[e("h1",{attrs:{id:"关于-monibuca"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#关于-monibuca","aria-hidden":"true"}},[this._v("#")]),this._v(" 关于 Monibuca")])])}],!1,null,null,null);e.default=s.exports}}]);

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{167:function(t,e,s){"use strict";s.r(e);var a=s(0),i=Object(a.a)({},(function(){var t=this.$createElement;this._self._c;return this._m(0)}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[e("h1",{attrs:{id:"page-2"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#page-2","aria-hidden":"true"}},[this._v("#")]),this._v(" Page 2")]),e("p",[this._v("this is a second page")])])}],!1,null,null,null);e.default=i.exports}}]);

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{166:function(t,n,e){"use strict";e.r(n);var s=e(0),c=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("div",{staticClass:"content"})}),[],!1,null,null,null);n.default=c.exports}}]);

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{165:function(t,e,i){"use strict";i.r(e);var s=i(0),a=Object(s.a)({},(function(){var t=this.$createElement;this._self._c;return this._m(0)}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[e("h1",{attrs:{id:"page-3-with-custom-link-page"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#page-3-with-custom-link-page","aria-hidden":"true"}},[this._v("#")]),this._v(" Page 3 with custom link page")]),e("p",[this._v("this is a third page")])])}],!1,null,null,null);e.default=a.exports}}]);

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{164:function(t,e,s){"use strict";s.r(e);var a=s(0),i=Object(a.a)({},(function(){var t=this.$createElement;this._self._c;return this._m(0)}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[e("h1",{attrs:{id:"page-1"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#page-1","aria-hidden":"true"}},[this._v("#")]),this._v(" Page 1")]),e("p",[this._v("this is a page")])])}],!1,null,null,null);e.default=i.exports}}]);

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{163:function(t,n,e){"use strict";e.r(n);var s=e(0),c=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("div",{staticClass:"content"})}),[],!1,null,null,null);n.default=c.exports}}]);

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{162:function(t,s,a){"use strict";a.r(s);var e=a(0),i=Object(e.a)({},(function(){var t=this.$createElement;this._self._c;return this._m(0)}),[function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"content"},[s("h1",{attrs:{id:"jessica"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#jessica","aria-hidden":"true"}},[this._v("#")]),this._v(" Jessica")]),s("h2",{attrs:{id:"配置"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#配置","aria-hidden":"true"}},[this._v("#")]),this._v(" 配置")])])}],!1,null,null,null);s.default=i.exports}}]);

File diff suppressed because one or more lines are too long

View File

@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Page 2 | Monibuca</title>
<meta name="description" content="">
<link rel="preload" href="/docs/assets/css/styles.92893aed.css" as="style"><link rel="preload" href="/docs/assets/js/app.92893aed.js" as="script"><link rel="preload" href="/docs/assets/js/2.a6d3efaf.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.685ed1cc.js"><link rel="prefetch" href="/docs/assets/js/3.a9fbea98.js"><link rel="prefetch" href="/docs/assets/js/4.727e40e9.js"><link rel="prefetch" href="/docs/assets/js/5.78b155e8.js"><link rel="prefetch" href="/docs/assets/js/6.35a311c9.js"><link rel="prefetch" href="/docs/assets/js/7.ab3a52c1.js">
<link rel="stylesheet" href="/docs/assets/css/styles.92893aed.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div><a href="/docs/" class="home-link router-link-active"><!----><span class="site-name">
Monibuca
</span></a><div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""><!----></div><!----></div></header><div class="sidebar-mask"></div><div class="sidebar"><!----><ul class="sidebar-links"><li><a href="/docs/" class="sidebar-link">介绍</a></li><li><a href="/docs/install.html" class="sidebar-link">安装</a></li><li><a href="/docs/config.html" class="active sidebar-link">配置</a></li><li><a href="/docs/develop.html" class="sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="sidebar-link">更新日志</a></li><li><div class="sidebar-group collapsable"><p class="sidebar-heading"><span>内置插件</span><span class="arrow right"></span></p><!----></div></li></ul></div><div class="page"><div class="content"><h1 id="page-2"><a href="#page-2" aria-hidden="true" class="header-anchor">#</a> Page 2</h1><p>this is a second page</p></div><div class="page-edit"><!----><!----></div><div class="page-nav"><p class="inner"><span class="prev">
<a href="/docs/install.html" class="prev">
安装
</a></span><span class="next"><a href="/docs/develop.html">
插件开发
</a>
</span></p></div></div></div></div>
<script src="/docs/assets/js/app.92893aed.js" defer></script><script src="/docs/assets/js/2.a6d3efaf.js" defer></script>
</body>
</html>

View File

@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Monibuca</title>
<meta name="description" content="">
<link rel="preload" href="/docs/assets/css/styles.92893aed.css" as="style"><link rel="preload" href="/docs/assets/js/app.92893aed.js" as="script"><link rel="preload" href="/docs/assets/js/3.a9fbea98.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.685ed1cc.js"><link rel="prefetch" href="/docs/assets/js/2.a6d3efaf.js"><link rel="prefetch" href="/docs/assets/js/4.727e40e9.js"><link rel="prefetch" href="/docs/assets/js/5.78b155e8.js"><link rel="prefetch" href="/docs/assets/js/6.35a311c9.js"><link rel="prefetch" href="/docs/assets/js/7.ab3a52c1.js">
<link rel="stylesheet" href="/docs/assets/css/styles.92893aed.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div><a href="/docs/" class="home-link router-link-active"><!----><span class="site-name">
Monibuca
</span></a><div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""><!----></div><!----></div></header><div class="sidebar-mask"></div><div class="sidebar"><!----><ul class="sidebar-links"><li><a href="/docs/" class="sidebar-link">介绍</a></li><li><a href="/docs/install.html" class="sidebar-link">安装</a></li><li><a href="/docs/config.html" class="sidebar-link">配置</a></li><li><a href="/docs/develop.html" class="active sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="sidebar-link">更新日志</a></li><li><div class="sidebar-group collapsable"><p class="sidebar-heading"><span>内置插件</span><span class="arrow right"></span></p><!----></div></li></ul></div><div class="page"><div class="content"></div><div class="page-edit"><!----><!----></div><div class="page-nav"><p class="inner"><span class="prev">
<a href="/docs/config.html" class="prev">
配置
</a></span><span class="next"><a href="/docs/history.html">
更新日志
</a>
</span></p></div></div></div></div>
<script src="/docs/assets/js/app.92893aed.js" defer></script><script src="/docs/assets/js/3.a9fbea98.js" defer></script>
</body>
</html>

View File

@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Page 3 with custom link page | Monibuca</title>
<meta name="description" content="">
<link rel="preload" href="/docs/assets/css/styles.92893aed.css" as="style"><link rel="preload" href="/docs/assets/js/app.92893aed.js" as="script"><link rel="preload" href="/docs/assets/js/4.727e40e9.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.685ed1cc.js"><link rel="prefetch" href="/docs/assets/js/2.a6d3efaf.js"><link rel="prefetch" href="/docs/assets/js/3.a9fbea98.js"><link rel="prefetch" href="/docs/assets/js/5.78b155e8.js"><link rel="prefetch" href="/docs/assets/js/6.35a311c9.js"><link rel="prefetch" href="/docs/assets/js/7.ab3a52c1.js">
<link rel="stylesheet" href="/docs/assets/css/styles.92893aed.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div><a href="/docs/" class="home-link router-link-active"><!----><span class="site-name">
Monibuca
</span></a><div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""><!----></div><!----></div></header><div class="sidebar-mask"></div><div class="sidebar"><!----><ul class="sidebar-links"><li><a href="/docs/" class="sidebar-link">介绍</a></li><li><a href="/docs/install.html" class="sidebar-link">安装</a></li><li><a href="/docs/config.html" class="sidebar-link">配置</a></li><li><a href="/docs/develop.html" class="sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="active sidebar-link">更新日志</a></li><li><div class="sidebar-group collapsable"><p class="sidebar-heading"><span>内置插件</span><span class="arrow right"></span></p><!----></div></li></ul></div><div class="page"><div class="content"><h1 id="page-3-with-custom-link-page"><a href="#page-3-with-custom-link-page" aria-hidden="true" class="header-anchor">#</a> Page 3 with custom link page</h1><p>this is a third page</p></div><div class="page-edit"><!----><!----></div><div class="page-nav"><p class="inner"><span class="prev">
<a href="/docs/develop.html" class="prev">
插件开发
</a></span><span class="next"><a href="/docs/plugins/jessica.html">
Jessica
</a>
</span></p></div></div></div></div>
<script src="/docs/assets/js/app.92893aed.js" defer></script><script src="/docs/assets/js/4.727e40e9.js" defer></script>
</body>
</html>

View File

@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>关于 Monibuca | Monibuca</title>
<meta name="description" content="">
<link rel="preload" href="/docs/assets/css/styles.92893aed.css" as="style"><link rel="preload" href="/docs/assets/js/app.92893aed.js" as="script"><link rel="preload" href="/docs/assets/js/1.685ed1cc.js" as="script"><link rel="prefetch" href="/docs/assets/js/2.a6d3efaf.js"><link rel="prefetch" href="/docs/assets/js/3.a9fbea98.js"><link rel="prefetch" href="/docs/assets/js/4.727e40e9.js"><link rel="prefetch" href="/docs/assets/js/5.78b155e8.js"><link rel="prefetch" href="/docs/assets/js/6.35a311c9.js"><link rel="prefetch" href="/docs/assets/js/7.ab3a52c1.js">
<link rel="stylesheet" href="/docs/assets/css/styles.92893aed.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div><a href="/docs/" class="home-link router-link-exact-active router-link-active"><!----><span class="site-name">
Monibuca
</span></a><div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""><!----></div><!----></div></header><div class="sidebar-mask"></div><div class="sidebar"><!----><ul class="sidebar-links"><li><a href="/docs/" class="active sidebar-link">介绍</a></li><li><a href="/docs/install.html" class="sidebar-link">安装</a></li><li><a href="/docs/config.html" class="sidebar-link">配置</a></li><li><a href="/docs/develop.html" class="sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="sidebar-link">更新日志</a></li><li><div class="sidebar-group collapsable"><p class="sidebar-heading"><span>内置插件</span><span class="arrow right"></span></p><!----></div></li></ul></div><div class="page"><div class="content"><h1 id="关于-monibuca"><a href="#关于-monibuca" aria-hidden="true" class="header-anchor">#</a> 关于 Monibuca</h1></div><div class="page-edit"><!----><!----></div><div class="page-nav"><p class="inner"><!----><span class="next"><a href="/docs/install.html">
安装
</a>
</span></p></div></div></div></div>
<script src="/docs/assets/js/app.92893aed.js" defer></script><script src="/docs/assets/js/1.685ed1cc.js" defer></script>
</body>
</html>

View File

@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Page 1 | Monibuca</title>
<meta name="description" content="">
<link rel="preload" href="/docs/assets/css/styles.92893aed.css" as="style"><link rel="preload" href="/docs/assets/js/app.92893aed.js" as="script"><link rel="preload" href="/docs/assets/js/5.78b155e8.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.685ed1cc.js"><link rel="prefetch" href="/docs/assets/js/2.a6d3efaf.js"><link rel="prefetch" href="/docs/assets/js/3.a9fbea98.js"><link rel="prefetch" href="/docs/assets/js/4.727e40e9.js"><link rel="prefetch" href="/docs/assets/js/6.35a311c9.js"><link rel="prefetch" href="/docs/assets/js/7.ab3a52c1.js">
<link rel="stylesheet" href="/docs/assets/css/styles.92893aed.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div><a href="/docs/" class="home-link router-link-active"><!----><span class="site-name">
Monibuca
</span></a><div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""><!----></div><!----></div></header><div class="sidebar-mask"></div><div class="sidebar"><!----><ul class="sidebar-links"><li><a href="/docs/" class="sidebar-link">介绍</a></li><li><a href="/docs/install.html" class="active sidebar-link">安装</a></li><li><a href="/docs/config.html" class="sidebar-link">配置</a></li><li><a href="/docs/develop.html" class="sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="sidebar-link">更新日志</a></li><li><div class="sidebar-group collapsable"><p class="sidebar-heading"><span>内置插件</span><span class="arrow right"></span></p><!----></div></li></ul></div><div class="page"><div class="content"><h1 id="page-1"><a href="#page-1" aria-hidden="true" class="header-anchor">#</a> Page 1</h1><p>this is a page</p></div><div class="page-edit"><!----><!----></div><div class="page-nav"><p class="inner"><span class="prev">
<a href="/docs/" class="prev router-link-active">
介绍
</a></span><span class="next"><a href="/docs/config.html">
配置
</a>
</span></p></div></div></div></div>
<script src="/docs/assets/js/app.92893aed.js" defer></script><script src="/docs/assets/js/5.78b155e8.js" defer></script>
</body>
</html>

View File

@@ -1,19 +0,0 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Monibuca</title>
<meta name="description" content="">
<link rel="preload" href="/docs/assets/css/styles.92893aed.css" as="style"><link rel="preload" href="/docs/assets/js/app.92893aed.js" as="script"><link rel="preload" href="/docs/assets/js/6.35a311c9.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.685ed1cc.js"><link rel="prefetch" href="/docs/assets/js/2.a6d3efaf.js"><link rel="prefetch" href="/docs/assets/js/3.a9fbea98.js"><link rel="prefetch" href="/docs/assets/js/4.727e40e9.js"><link rel="prefetch" href="/docs/assets/js/5.78b155e8.js"><link rel="prefetch" href="/docs/assets/js/7.ab3a52c1.js">
<link rel="stylesheet" href="/docs/assets/css/styles.92893aed.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div><a href="/docs/" class="home-link router-link-active"><!----><span class="site-name">
Monibuca
</span></a><div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""><!----></div><!----></div></header><div class="sidebar-mask"></div><div class="sidebar"><!----><ul class="sidebar-links"><li><a href="/docs/" class="sidebar-link">介绍</a></li><li><a href="/docs/install.html" class="sidebar-link">安装</a></li><li><a href="/docs/config.html" class="sidebar-link">配置</a></li><li><a href="/docs/develop.html" class="sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="sidebar-link">更新日志</a></li><li><div class="sidebar-group collapsable"><p class="sidebar-heading"><span>内置插件</span><span class="arrow right"></span></p><!----></div></li></ul></div><div class="page"><div class="content"></div><div class="page-edit"><!----><!----></div><!----></div></div></div>
<script src="/docs/assets/js/app.92893aed.js" defer></script><script src="/docs/assets/js/6.35a311c9.js" defer></script>
</body>
</html>

View File

@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Jessica | Monibuca</title>
<meta name="description" content="">
<link rel="preload" href="/docs/assets/css/styles.92893aed.css" as="style"><link rel="preload" href="/docs/assets/js/app.92893aed.js" as="script"><link rel="preload" href="/docs/assets/js/7.ab3a52c1.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.685ed1cc.js"><link rel="prefetch" href="/docs/assets/js/2.a6d3efaf.js"><link rel="prefetch" href="/docs/assets/js/3.a9fbea98.js"><link rel="prefetch" href="/docs/assets/js/4.727e40e9.js"><link rel="prefetch" href="/docs/assets/js/5.78b155e8.js"><link rel="prefetch" href="/docs/assets/js/6.35a311c9.js">
<link rel="stylesheet" href="/docs/assets/css/styles.92893aed.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div><a href="/docs/" class="home-link router-link-active"><!----><span class="site-name">
Monibuca
</span></a><div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""><!----></div><!----></div></header><div class="sidebar-mask"></div><div class="sidebar"><!----><ul class="sidebar-links"><li><a href="/docs/" class="sidebar-link">介绍</a></li><li><a href="/docs/install.html" class="sidebar-link">安装</a></li><li><a href="/docs/config.html" class="sidebar-link">配置</a></li><li><a href="/docs/develop.html" class="sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="sidebar-link">更新日志</a></li><li><div class="sidebar-group collapsable"><p class="sidebar-heading open"><span>内置插件</span><span class="arrow down"></span></p><ul class="sidebar-group-items"><li><a href="/docs/plugins/jessica.html" class="active sidebar-link">Jessica</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/docs/plugins/jessica.html#配置" class="sidebar-link">配置</a></li></ul></li></ul></div></li></ul></div><div class="page"><div class="content"><h1 id="jessica"><a href="#jessica" aria-hidden="true" class="header-anchor">#</a> Jessica</h1><h2 id="配置"><a href="#配置" aria-hidden="true" class="header-anchor">#</a> 配置</h2></div><div class="page-edit"><!----><!----></div><div class="page-nav"><p class="inner"><span class="prev">
<a href="/docs/history.html" class="prev">
更新日志
</a></span><!----></p></div></div></div></div>
<script src="/docs/assets/js/app.92893aed.js" defer></script><script src="/docs/assets/js/7.ab3a52c1.js" defer></script>
</body>
</html>

View File

@@ -1,96 +0,0 @@
/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
* See https://goo.gl/nhQhGp
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
* See https://goo.gl/2aRDsh
*/
importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js");
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://goo.gl/S9QRab
*/
self.__precacheManifest = [
{
"url": "404.html",
"revision": "0e4ff0fd403c5d29a13752bf3ef14d6d"
},
{
"url": "assets/css/styles.92893aed.css",
"revision": "07fa9a1fb782ef296585900714fac621"
},
{
"url": "assets/img/search.83621669.svg",
"revision": "83621669651b9a3d4bf64d1a670ad856"
},
{
"url": "assets/js/1.685ed1cc.js",
"revision": "97247c4d4a60db87b22488bfdf99197d"
},
{
"url": "assets/js/2.a6d3efaf.js",
"revision": "dca15d8c2b94dadcdce4386e7d628716"
},
{
"url": "assets/js/3.a9fbea98.js",
"revision": "7f6bca508f94f8f508a61cd05b582084"
},
{
"url": "assets/js/4.727e40e9.js",
"revision": "b8fca87e9c559c1fe3fa03b76d15c3bd"
},
{
"url": "assets/js/5.78b155e8.js",
"revision": "73d1f3053737ad68ee4ec4fa395fcae9"
},
{
"url": "assets/js/6.35a311c9.js",
"revision": "8bd2ad3294cf6a29e7326ca860d43250"
},
{
"url": "assets/js/7.ab3a52c1.js",
"revision": "437c1f4739f884dcff81135f7bc8450c"
},
{
"url": "assets/js/app.92893aed.js",
"revision": "6962a63ee86d8a65c9ae46100427d812"
},
{
"url": "config.html",
"revision": "73b0335d99419df53f57913cd7303509"
},
{
"url": "develop.html",
"revision": "bc1b3cad3f88b61fee351464cffc7d0f"
},
{
"url": "history.html",
"revision": "dc6b1088d3cc72f1f3a402c56a441a57"
},
{
"url": "index.html",
"revision": "577c237555d953d8a22d045212f0a92e"
},
{
"url": "install.html",
"revision": "d06af57877e4695ae1a81528e2f128f1"
},
{
"url": "plugins/index.html",
"revision": "e578381fed44a65589053470ea33795d"
},
{
"url": "plugins/jessica.html",
"revision": "fe406bf63d2d349727c63d6045fd2efa"
}
].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});

View File

@@ -0,0 +1,88 @@
<template>
<Modal v-bind="$attrs" draggable v-on="$listeners" title="录制的视频">
<List>
<ListItem v-for="item in data" :key="item">
<ListItemMeta :title="item.Path">
<template slot="description">{{toSizeStr(item.Size)}} {{toDurationStr(item.Duration)}}</template>
</ListItemMeta>
<template slot="action">
<li>
<a href="javascript:void(0)" @click="play(item)">Play</a>
</li>
<li>
<a href="javascript:void(0)">Delete</a>
</li>
</template>
</ListItem>
</List>
</Modal>
</template>
<script>
const uintInc = {
"": "K",
K: "M",
M: "G",
G: null
};
export default {
data() {
return {
data: []
};
},
methods: {
play(item) {
window.ajax.get(
"//" + location.host + "/api/record/flv/play",
{ streamPath: item.Path.replace(".flv", "") },
x => {
if (x == "success") {
this.$Message.success("开始发布");
} else {
this.$Message.error(x);
}
}
);
},
toSizeStr(value, unit = "") {
if (value > 1024 && uintInc[unit]) {
return this.toSizeStr(value / 1024, uintInc[unit]);
}
return value.toFixed(2).replace(".00", "") + unit + "B";
},
toDurationStr(value) {
if (value > 1000) {
let s = value / 1000;
if (s > 60) {
s = s | 0;
let min = (s / 60) >> 0;
if (min > 60) {
let hour = (min / 60) >> 0;
return hour + "hour" + (min % 60) + "min";
} else {
return min + "min" + (s % 60) + "s";
}
} else {
return s.toFixed(3) + "s";
}
} else {
return value + "ms";
}
}
},
mounted() {
window.ajax.getJSON(
"//" + location.host + "/api/record/flv/list",
{},
x => {
this.data = x;
}
);
}
};
</script>
<style>
</style>

View File

@@ -1,240 +1,261 @@
<template>
<div class="layout">
<ButtonGroup vertical>
<Button icon="ios-folder"></Button>
<Button icon="md-bug"></Button>
<Button icon="md-settings"></Button>
</ButtonGroup>
<Card v-for="item in Rooms" :key="item.StreamPath" class="room">
<p slot="title">
{{typeMap[item.Type]||item.Type}}{{item.StreamPath}}
</p>
<StartTime slot="extra" :value="item.StartTime"></StartTime>
<p>
{{SoundFormat(item.AudioInfo.SoundFormat)}} {{item.AudioInfo.PacketCount}}
{{SoundRate(item.AudioInfo.SoundRate)}} 声道:{{item.AudioInfo.SoundType}}
</p>
<p>
{{CodecID(item.VideoInfo.CodecID)}} {{item.VideoInfo.PacketCount}}
{{item.VideoInfo.SPSInfo.Width}}x{{item.VideoInfo.SPSInfo.Height}}
</p>
<ButtonGroup size="small">
<Button @click="onShowDetail(item)" icon="ios-people">
{{item.SubscriberInfo?item.SubscriberInfo.length:0}}
</Button>
<Button v-if="item.Type" @click="preview(item)" icon="md-eye">
</Button>
<Button @click="stopRecord(item)" class="recording" v-if="isRecording(item)" icon="ios-radio-button-on">
</Button>
<Button @click="record(item)" v-else icon="ios-radio-button-on">
</Button>
</ButtonGroup>
</Card>
<div v-if="Rooms.length==0" class="empty">
<Icon type="md-wine" size="50"/>
没有任何房间
</div>
<div class="status">
<Alert>
带宽消耗 📥{{totalInNetSpeed}} 📤{{totalOutNetSpeed}}
</Alert>
<Alert :type="memoryStatus">
内存使用{{networkFormat(Memory.Used,"M")}} 占比{{Memory.Usage.toFixed(2)}}%
</Alert>
<Alert :type="cpuStatus">
CPU使用{{CPUUsage.toFixed(2)}}%
</Alert>
<Alert :type="hardDiskStatus">
磁盘使用{{networkFormat(HardDisk.Used,"M")}} 占比{{HardDisk.Usage.toFixed(2)}}%
</Alert>
</div>
<Jessibuca ref="jessibuca" v-model="showPreview"></Jessibuca>
<div class="layout">
<ButtonGroup vertical>
<Button icon="ios-folder" @click="showRecords=true"></Button>
<Button icon="md-bug"></Button>
<Button icon="md-settings"></Button>
</ButtonGroup>
<Card v-for="item in Rooms" :key="item.StreamPath" class="room">
<p slot="title">{{typeMap[item.Type]||item.Type}}{{item.StreamPath}}</p>
<StartTime slot="extra" :value="item.StartTime"></StartTime>
<p>
{{SoundFormat(item.AudioInfo.SoundFormat)}} {{item.AudioInfo.PacketCount}}
{{SoundRate(item.AudioInfo.SoundRate)}} 声道:{{item.AudioInfo.SoundType}}
</p>
<p>
{{CodecID(item.VideoInfo.CodecID)}} {{item.VideoInfo.PacketCount}}
{{item.VideoInfo.SPSInfo.Width}}x{{item.VideoInfo.SPSInfo.Height}}
</p>
<ButtonGroup size="small">
<Button
@click="onShowDetail(item)"
icon="ios-people"
>{{item.SubscriberInfo?item.SubscriberInfo.length:0}}</Button>
<Button v-if="item.Type" @click="preview(item)" icon="md-eye"></Button>
<Button
@click="stopRecord(item)"
class="recording"
v-if="isRecording(item)"
icon="ios-radio-button-on"
></Button>
<Button @click="record(item)" v-else icon="ios-radio-button-on"></Button>
</ButtonGroup>
</Card>
<div v-if="Rooms.length==0" class="empty">
<Icon type="md-wine" size="50" />没有任何房间
</div>
<div class="status">
<Alert>带宽消耗 📥{{totalInNetSpeed}} 📤{{totalOutNetSpeed}}</Alert>
<Alert
:type="memoryStatus"
>内存使用{{networkFormat(Memory.Used,"M")}} 占比{{Memory.Usage.toFixed(2)}}%</Alert>
<Alert :type="cpuStatus">CPU使用{{CPUUsage.toFixed(2)}}%</Alert>
<Alert
:type="hardDiskStatus"
>磁盘使用{{networkFormat(HardDisk.Used,"M")}} 占比{{HardDisk.Usage.toFixed(2)}}%</Alert>
</div>
<Jessibuca ref="jessibuca" v-model="showPreview"></Jessibuca>
<Records v-model="showRecords" />
</div>
</template>
<script>
import {mapActions, mapState} from 'vuex'
import Jessibuca from "../components/Jessibuca";
import StartTime from "../components/StartTime";
const uintInc = {
"": "K",
K: "M",
M: "G",
G: null
}
const SoundFormat = {
0: "Linear PCM, platform endian",
1: "ADPCM",
2: "MP3",
3: "Linear PCM, little endian",
4: "Nellymoser 16kHz mono",
5: "Nellymoser 8kHz mono",
6: "Nellymoser",
7: "G.711 A-law logarithmic PCM",
8: "G.711 mu-law logarithmic PCM",
9: "reserved",
10: "AAC",
11: "Speex",
14: "MP3 8Khz",
15: "Device-specific sound"
}
const CodecID = {
1: "JPEG (currently unused)",
2: "Sorenson H.263",
3: "Screen video",
4: "On2 VP6",
5: "On2 VP6 with alpha channel",
6: "Screen video version 2",
7: "AVC",
12: "H265"
}
export default {
name: "Console",
components: {
Jessibuca, StartTime
},
data() {
return {
showPreview: false,
typeMap: {
TS: "🎬", HLS: "🍎", "": "⏳", Match365: "🏆", RTMP: "📸"
}
}
},
computed: {
...mapState({
Rooms: state => state.summary.Rooms || [],
Memory: state => state.summary.Memory,
CPUUsage: state => state.summary.CPUUsage,
HardDisk: state => state.summary.HardDisk,
cpuStatus: state => {
if (state.summary.CPUUsage > 99)
return "error"
return state.summary.CPUUsage > 50 ? "warning" : "success"
},
memoryStatus(state) {
if (state.summary.CPUUsage > 99)
return "error"
return state.summary.CPUUsage > 50 ? "warning" : "success"
},
hardDiskStatus(state) {
if (state.summary.CPUUsage > 99)
return "error"
return state.summary.CPUUsage > 50 ? "warning" : "success"
},
totalInNetSpeed(state) {
return this.networkFormat(state.summary.NetWork.reduce((aac, c) => aac + c.ReceiveSpeed, 0)) + "/S"
},
totalOutNetSpeed(state) {
return this.networkFormat(state.summary.NetWork.reduce((aac, c) => aac + c.SentSpeed, 0)) + "/S"
}
}),
},
methods: {
...mapActions([
'fetchSummary',
'stopFetchSummary'
]),
preview(item) {
this.$refs.jessibuca.play("ws://" + location.hostname + ":8080/" + item.StreamPath)
this.showPreview = true
}, onShowDetail() {
// this.showDetail = true
// this.currentSub = item
},
networkFormat(value, unit = "") {
if (value > 1024 && uintInc[unit]) {
return this.networkFormat(value / 1024, uintInc[unit])
}
return value.toFixed(2).replace(".00", "") + unit + "B"
},
SoundFormat(soundFormat) {
return SoundFormat[soundFormat]
},
CodecID(codec) {
return CodecID[codec]
},
SoundRate(rate) {
return rate > 1000 ? (rate / 1000) + "kHz" : rate + "Hz"
},
record(item) {
window.ajax.get("//" + location.host + "/api/record/flv", {streamPath: item.StreamPath}, x => {
if (x == "success") {
this.$Message.success('开始录制');
} else {
this.$Message.error(x);
}
})
},
stopRecord(item) {
window.ajax.get("//" + location.host + "/api/record/flv/stop", {streamPath: item.StreamPath}, x => {
if (x == "success") {
this.$Message.success('停止录制');
} else {
this.$Message.error(x);
}
})
},
isRecording(item) {
return item.SubscriberInfo && item.SubscriberInfo.find(x => x.Type == "FlvRecord")
}
},
mounted() {
this.fetchSummary()
},
destroyed() {
this.stopFetchSummary()
import { mapActions, mapState } from "vuex";
import Jessibuca from "../components/Jessibuca";
import StartTime from "../components/StartTime";
import Records from "../components/Records";
const uintInc = {
"": "K",
K: "M",
M: "G",
G: null
};
const SoundFormat = {
0: "Linear PCM, platform endian",
1: "ADPCM",
2: "MP3",
3: "Linear PCM, little endian",
4: "Nellymoser 16kHz mono",
5: "Nellymoser 8kHz mono",
6: "Nellymoser",
7: "G.711 A-law logarithmic PCM",
8: "G.711 mu-law logarithmic PCM",
9: "reserved",
10: "AAC",
11: "Speex",
14: "MP3 8Khz",
15: "Device-specific sound"
};
const CodecID = {
1: "JPEG (currently unused)",
2: "Sorenson H.263",
3: "Screen video",
4: "On2 VP6",
5: "On2 VP6 with alpha channel",
6: "Screen video version 2",
7: "AVC",
12: "H265"
};
export default {
name: "Console",
components: {
Jessibuca,
StartTime,
Records
},
data() {
return {
showPreview: false,
showRecords: false,
typeMap: {
FlvFile:"🎥",
TS: "🎬",
HLS: "🍎",
"": "⏳",
Match365: "🏆",
RTMP: "🚠"
}
};
},
computed: {
...mapState({
Rooms: state => state.summary.Rooms || [],
Memory: state => state.summary.Memory,
CPUUsage: state => state.summary.CPUUsage,
HardDisk: state => state.summary.HardDisk,
cpuStatus: state => {
if (state.summary.CPUUsage > 99) return "error";
return state.summary.CPUUsage > 50 ? "warning" : "success";
},
memoryStatus(state) {
if (state.summary.CPUUsage > 99) return "error";
return state.summary.CPUUsage > 50 ? "warning" : "success";
},
hardDiskStatus(state) {
if (state.summary.CPUUsage > 99) return "error";
return state.summary.CPUUsage > 50 ? "warning" : "success";
},
totalInNetSpeed(state) {
return (
this.networkFormat(
state.summary.NetWork.reduce((aac, c) => aac + c.ReceiveSpeed, 0)
) + "/S"
);
},
totalOutNetSpeed(state) {
return (
this.networkFormat(
state.summary.NetWork.reduce((aac, c) => aac + c.SentSpeed, 0)
) + "/S"
);
}
})
},
methods: {
...mapActions(["fetchSummary", "stopFetchSummary"]),
preview(item) {
this.$refs.jessibuca.play(
"ws://" + location.hostname + ":8080/" + item.StreamPath
);
this.showPreview = true;
},
onShowDetail() {
// this.showDetail = true
// this.currentSub = item
},
networkFormat(value, unit = "") {
if (value > 1024 && uintInc[unit]) {
return this.networkFormat(value / 1024, uintInc[unit]);
}
return value.toFixed(2).replace(".00", "") + unit + "B";
},
SoundFormat(soundFormat) {
return SoundFormat[soundFormat];
},
CodecID(codec) {
return CodecID[codec];
},
SoundRate(rate) {
return rate > 1000 ? rate / 1000 + "kHz" : rate + "Hz";
},
record(item) {
window.ajax.get(
"//" + location.host + "/api/record/flv",
{ streamPath: item.StreamPath },
x => {
if (x == "success") {
this.$Message.success("开始录制");
} else {
this.$Message.error(x);
}
}
);
},
stopRecord(item) {
window.ajax.get(
"//" + location.host + "/api/record/flv/stop",
{ streamPath: item.StreamPath },
x => {
if (x == "success") {
this.$Message.success("停止录制");
} else {
this.$Message.error(x);
}
}
);
},
isRecording(item) {
return (
item.SubscriberInfo &&
item.SubscriberInfo.find(x => x.Type == "FlvRecord")
);
}
},
mounted() {
this.fetchSummary();
},
destroyed() {
this.stopFetchSummary();
}
};
</script>
<style scoped>
@keyframes recording {
0% {
opacity: 0.2;
}
50% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
@keyframes recording {
0% {
opacity: 0.2;
}
50% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
.recording {
animation: recording 1s infinite;
}
.recording {
animation: recording 1s infinite;
}
.layout {
padding-bottom: 30px;
display: flex;
flex-wrap: wrap;
}
.layout {
padding-bottom: 30px;
display: flex;
flex-wrap: wrap;
}
.room {
width: 250px;
margin: 10px;
text-align: left;
}
.room {
width: 250px;
margin: 10px;
text-align: left;
}
.empty {
color: #eb5e46;
width: 100%;
min-height: 500px;
display: flex;
justify-content: center;
align-items: center;
}
.empty {
color: #eb5e46;
width: 100%;
min-height: 500px;
display: flex;
justify-content: center;
align-items: center;
}
.status {
position: fixed;
display: flex;
left: 5px;
bottom: 10px;
}
.status {
position: fixed;
display: flex;
left: 5px;
bottom: 10px;
}
.status > div {
margin: 0 5px;
}
.status > div {
margin: 0 5px;
}
</style>

View File

@@ -96,18 +96,18 @@ func WriteFLVTag(w io.Writer, tag *pool.SendPacket) (err error) {
func ReadFLVTag(r io.Reader) (tag *pool.AVPacket, err error) {
head := pool.GetSlice(11)
defer pool.RecycleSlice(head)
if _, err = r.Read(head); err != nil {
if _, err = io.ReadFull(r, head); err != nil {
return
}
av := pool.NewAVPacket(head[0])
tag = pool.NewAVPacket(head[0])
dataSize := util.BigEndian.Uint24(head[1:])
av.Timestamp = util.BigEndian.Uint24(head[4:])
tag.Timestamp = util.BigEndian.Uint24(head[4:])
body := pool.GetSlice(int(dataSize))
defer pool.RecycleSlice(body)
if _, err = r.Read(body); err == nil {
av.Payload = body
if _, err = io.ReadFull(r, body); err == nil {
tag.Payload = body
t := pool.GetSlice(4)
_, err = r.Read(t)
_, err = io.ReadFull(r, t)
pool.RecycleSlice(t)
}
return

View File

@@ -154,7 +154,7 @@ func (r *Room) Run() {
func (r *Room) PushAudio(audio *pool.AVPacket) {
if audio.Payload[0] == 0xFF && (audio.Payload[1]&0xF0) == 0xF0 {
audio.IsADTS = true
r.AudioTag = audio
} else if r.AudioTag == nil {
audio.IsAACSequence = true
r.AudioTag = audio

View File

@@ -84,6 +84,19 @@ func init() {
})
}
func run() {
http.HandleFunc("/api/stop", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
if streamPath := r.URL.Query().Get("stream"); streamPath != "" {
if b, ok := AllRoom.Load(streamPath); ok {
b.(*Room).Cancel()
w.Write([]byte("success"))
} else {
w.Write([]byte("no query stream"))
}
} else {
w.Write([]byte("no such stream"))
}
})
http.HandleFunc("/api/summary", summary)
http.HandleFunc("/", website)
log.Printf("server gateway start at %s", config.ListenAddr)

View File

@@ -10,6 +10,23 @@ import (
"path"
)
func getDuration(file *os.File) uint32 {
_, err := file.Seek(-4, io.SeekEnd)
if err == nil {
var tagSize uint32
if tagSize, err = util.ReadByteToUint32(file, true); err == nil {
_, err = file.Seek(-int64(tagSize)-4, io.SeekEnd)
if err == nil {
var tag *pool.AVPacket
tag, err = avformat.ReadFLVTag(file)
if err == nil {
return tag.Timestamp
}
}
}
}
return 0
}
func SaveFlv(streamPath string, append bool) error {
flag := os.O_CREATE
if append {
@@ -29,20 +46,8 @@ func SaveFlv(streamPath string, append bool) error {
p.ID = filePath
p.Type = "FlvRecord"
if append {
_, err = file.Seek(4, io.SeekEnd)
if err == nil {
var tagSize uint32
if tagSize, err = util.ReadByteToUint32(file, true); err == nil {
_, err = file.Seek(int64(tagSize+4), io.SeekEnd)
if err == nil {
var tag *pool.AVPacket
tag, err = avformat.ReadFLVTag(file)
if err == nil {
p.OffsetTime = tag.Timestamp
}
}
}
}
p.OffsetTime = getDuration(file)
file.Seek(0, io.SeekEnd)
} else {
_, err = file.Write(avformat.FLVHeader)
}

View File

@@ -1,10 +1,14 @@
package record
import (
. "github.com/langhuihui/monibuca/monica"
"encoding/json"
"net/http"
"os"
"path"
"strings"
"sync"
. "github.com/langhuihui/monibuca/monica"
)
var config = struct {
@@ -12,6 +16,12 @@ var config = struct {
}{}
var recordings = sync.Map{}
type FlvFileInfo struct {
Path string
Size int64
Duration uint32
}
func init() {
InstallPlugin(&PluginConfig{
Name: "RecordFlv",
@@ -21,9 +31,22 @@ func init() {
})
}
func run() {
OnSubscribeHooks.AddHook(onSubscribe)
if !strings.HasSuffix(config.Path, "/") {
config.Path = config.Path + "/"
}
http.HandleFunc("/api/record/flv/list", func(writer http.ResponseWriter, r *http.Request) {
if files, err := tree(config.Path, 0); err == nil {
var bytes []byte
if bytes, err = json.Marshal(files); err == nil {
writer.Write(bytes)
} else {
writer.Write([]byte("{\"err\":\"" + err.Error() + "\"}"))
}
} else {
writer.Write([]byte("{\"err\":\"" + err.Error() + "\"}"))
}
})
http.HandleFunc("/api/record/flv", func(writer http.ResponseWriter, r *http.Request) {
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
if err := SaveFlv(streamPath, r.URL.Query().Get("append") != ""); err != nil {
@@ -35,6 +58,7 @@ func run() {
writer.Write([]byte("no streamPath"))
}
})
http.HandleFunc("/api/record/flv/stop", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
@@ -50,4 +74,69 @@ func run() {
w.Write([]byte("no such stream"))
}
})
http.HandleFunc("/api/record/flv/play", func(writer http.ResponseWriter, r *http.Request) {
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
if err := PublishFlvFile(streamPath); err != nil {
writer.Write([]byte(err.Error()))
} else {
writer.Write([]byte("success"))
}
} else {
writer.Write([]byte("no streamPath"))
}
})
}
func onSubscribe(s *OutputStream) {
filePath := config.Path + s.StreamPath + ".flv"
if s.Publisher == nil && PathExists(filePath) {
PublishFlvFile(s.StreamPath)
}
}
func PathExists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}
func tree(dstPath string, level int) (files []*FlvFileInfo, err error) {
var dstF *os.File
dstF, err = os.Open(dstPath)
if err != nil {
return
}
defer dstF.Close()
fileInfo, err := dstF.Stat()
if err != nil {
return
}
if !fileInfo.IsDir() { //如果dstF是文件
if path.Ext(fileInfo.Name()) == ".flv" {
files = append(files, &FlvFileInfo{
Path: strings.TrimPrefix(strings.TrimPrefix(dstPath, config.Path), "/"),
Size: fileInfo.Size(),
Duration: getDuration(dstF),
})
}
return
} else { //如果dstF是文件夹
var dir []os.FileInfo
dir, err = dstF.Readdir(0) //获取文件夹下各个文件或文件夹的fileInfo
if err != nil {
return
}
for _, fileInfo = range dir {
var _files []*FlvFileInfo
_files, err = tree(dstPath+"/"+fileInfo.Name(), level+1)
if err != nil {
return
}
files = append(files, _files...)
}
return
}
}

View File

@@ -2,10 +2,12 @@ package record
import (
"errors"
. "github.com/langhuihui/monibuca/monica"
"github.com/langhuihui/monibuca/monica/avformat"
"io"
"os"
"time"
. "github.com/langhuihui/monibuca/monica"
"github.com/langhuihui/monibuca/monica/avformat"
)
type FlvFile struct {
@@ -15,16 +17,25 @@ type FlvFile struct {
func PublishFlvFile(streamPath string) error {
if file, err := os.Open(config.Path + streamPath + ".flv"); err == nil {
stream := FlvFile{}
stream.UseTimestamp = true
if stream.Publish(streamPath, &stream) {
defer stream.Close()
stream.UseTimestamp = true
file.Seek(int64(len(avformat.FLVHeader)), io.SeekStart)
var lastTime uint32
for {
if tag, err := avformat.ReadFLVTag(file); err == nil {
switch tag.Type {
case avformat.FLV_TAG_TYPE_AUDIO:
stream.PushAudio(tag)
case avformat.FLV_TAG_TYPE_VIDEO:
if tag.Timestamp != 0 {
if lastTime == 0 {
lastTime = tag.Timestamp
}
}
stream.PushVideo(tag)
time.Sleep(time.Duration(tag.Timestamp-lastTime) * time.Millisecond)
lastTime = tag.Timestamp
}
} else {
return err