mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-09-27 23:02:07 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ecb931ccb0 | ||
![]() |
13c357493f | ||
![]() |
87776b9540 | ||
![]() |
543d735c41 | ||
![]() |
966f9b9d0d | ||
![]() |
5eb7f03dec |
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.exe
|
||||
.vscode
|
||||
.idea
|
||||
resource
|
@@ -12,7 +12,7 @@ ListenAddr = ":81"
|
||||
#
|
||||
#[Plugins.Auth]
|
||||
#Key="www.monibuca.com"
|
||||
#[Plugins.RecordFlv]
|
||||
#Path="./resouce"
|
||||
[Plugins.RecordFlv]
|
||||
Path="./resource"
|
||||
[Plugins.QoS]
|
||||
Suffix = ["high","medium","low"]
|
2
dashboard/.gitignore
vendored
2
dashboard/.gitignore
vendored
@@ -1,6 +1,6 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
|
||||
public/docs
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
|
1
dashboard/dist/css/app.b4fc79e8.css
vendored
1
dashboard/dist/css/app.b4fc79e8.css
vendored
@@ -1 +0,0 @@
|
||||
#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-7b106554{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}@keyframes recording-data-v-7b106554{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}.recording[data-v-7b106554]{-webkit-animation:recording-data-v-7b106554 1s infinite;animation:recording-data-v-7b106554 1s infinite}.layout[data-v-7b106554]{padding-bottom:30px;position:relative}.room[data-v-7b106554]{width:250px;margin:10px;text-align:left}.empty[data-v-7b106554]{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-7b106554],.status[data-v-7b106554]{display:-webkit-box;display:-ms-flexbox;display:flex}.status[data-v-7b106554]{position:fixed;left:5px;bottom:10px}.status>div[data-v-7b106554]{margin:0 5px}
|
1
dashboard/dist/css/app.ea4656d8.css
vendored
Normal file
1
dashboard/dist/css/app.ea4656d8.css
vendored
Normal file
@@ -0,0 +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}.records[data-v-4eee1624]{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0 15px}.records>[data-v-4eee1624]{width:200px}.log-container{overflow-y:auto;max-height:500px}@-webkit-keyframes recording-data-v-f6113870{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}@keyframes recording-data-v-f6113870{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}.recording[data-v-f6113870]{-webkit-animation:recording-data-v-f6113870 1s infinite;animation:recording-data-v-f6113870 1s infinite}.layout[data-v-f6113870]{padding-bottom:30px;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.room[data-v-f6113870]{width:250px;margin:10px;text-align:left}.empty[data-v-f6113870]{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-f6113870],.status[data-v-f6113870]{display:-webkit-box;display:-ms-flexbox;display:flex}.status[data-v-f6113870]{position:fixed;left:5px;bottom:10px}.status>div[data-v-f6113870]{margin:0 5px}
|
8
dashboard/dist/docs/404.html
vendored
8
dashboard/dist/docs/404.html
vendored
@@ -7,11 +7,11 @@
|
||||
<meta name="description" content="">
|
||||
|
||||
|
||||
<link rel="preload" href="/docs/assets/css/styles.ad3166d6.css" as="style"><link rel="preload" href="/docs/assets/js/app.ad3166d6.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.83ce04b6.js"><link rel="prefetch" href="/docs/assets/js/2.142d04d2.js"><link rel="prefetch" href="/docs/assets/js/3.2b6c987b.js"><link rel="prefetch" href="/docs/assets/js/4.ad90d74a.js"><link rel="prefetch" href="/docs/assets/js/5.36121818.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.ad3166d6.css">
|
||||
<link rel="preload" href="/docs/assets/css/styles.d51c815b.css" as="style"><link rel="preload" href="/docs/assets/js/app.d51c815b.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.6babbc1d.js"><link rel="prefetch" href="/docs/assets/js/2.190ec46a.js"><link rel="prefetch" href="/docs/assets/js/3.7646e76c.js"><link rel="prefetch" href="/docs/assets/js/4.08fbc0d9.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.d51c815b.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" data-server-rendered="true"><div class="theme-container"><div class="content"><h1>404</h1><blockquote>There's nothing here.</blockquote><a href="/docs/" class="router-link-active">Take me home.</a></div></div></div>
|
||||
<script src="/docs/assets/js/app.ad3166d6.js" defer></script>
|
||||
<div id="app" data-server-rendered="true"><div class="theme-container"><div class="content"><h1>404</h1><blockquote>Looks like we've got some broken links.</blockquote><a href="/docs/" class="router-link-active">Take me home.</a></div></div></div>
|
||||
<script src="/docs/assets/js/app.d51c815b.js" defer></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
@@ -1 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{161:function(t,e,s){"use strict";s.r(e);var n=s(0),i=Object(n.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:"更新历史"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#更新历史","aria-hidden":"true"}},[this._v("#")]),this._v(" 更新历史")]),e("ul",[e("li",[this._v("2020/1/27\n1.0完成")])])])}],!1,null,null,null);e.default=i.exports}}]);
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{165:function(t,e,s){"use strict";s.r(e);var n=s(0),i=Object(n.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:"更新历史"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#更新历史","aria-hidden":"true"}},[this._v("#")]),this._v(" 更新历史")]),e("ul",[e("li",[this._v("2020/1/27\n初步完成")])])])}],!1,null,null,null);e.default=i.exports}}]);
|
1
dashboard/dist/docs/assets/js/4.08fbc0d9.js
vendored
Normal file
1
dashboard/dist/docs/assets/js/4.08fbc0d9.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dashboard/dist/docs/assets/js/4.ad90d74a.js
vendored
1
dashboard/dist/docs/assets/js/4.ad90d74a.js
vendored
@@ -1 +0,0 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{164: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}}]);
|
1
dashboard/dist/docs/assets/js/5.36121818.js
vendored
1
dashboard/dist/docs/assets/js/5.36121818.js
vendored
@@ -1 +0,0 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{165:function(s,a,t){"use strict";t.r(a);var e=t(0),n=Object(e.a)({},(function(){var s=this.$createElement;this._self._c;return this._m(0)}),[function(){var s=this,a=s.$createElement,t=s._self._c||a;return t("div",{staticClass:"content"},[t("h1",{attrs:{id:"jessica"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#jessica","aria-hidden":"true"}},[s._v("#")]),s._v(" Jessica")]),t("p",[s._v("该插件为基于WebSocket协议传输音视频的订阅者,音视频数据以裸数据的形式进行传输,我们需要Jessibuca播放器来进行播放\nJessibua播放器已内置于源码中,该播放器通过js解码H264并用canvas进行渲染,可以运行在几乎所有的终端浏览器上面。\n在Monibuca的Web界面中预览功能就是使用的Jessibuca播放器。")]),t("h2",{attrs:{id:"配置"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#配置","aria-hidden":"true"}},[s._v("#")]),s._v(" 配置")]),t("p",[s._v("目前仅有的配置是监听的端口号")]),t("div",{staticClass:"language-toml extra-class"},[t("pre",{pre:!0,attrs:{class:"language-toml"}},[t("code",[t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("[")]),t("span",{pre:!0,attrs:{class:"token table class-name"}},[s._v("Plugins.Jessica")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("]")]),s._v("\n"),t("span",{pre:!0,attrs:{class:"token key property"}},[s._v("ListenAddr")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[s._v("=")]),s._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[s._v('":8080"')]),s._v("\n")])])])])}],!1,null,null,null);a.default=n.exports}}]);
|
File diff suppressed because one or more lines are too long
8
dashboard/dist/docs/assets/js/app.d51c815b.js
vendored
Normal file
8
dashboard/dist/docs/assets/js/app.d51c815b.js
vendored
Normal file
File diff suppressed because one or more lines are too long
26
dashboard/dist/docs/develop.html
vendored
26
dashboard/dist/docs/develop.html
vendored
@@ -7,13 +7,13 @@
|
||||
<meta name="description" content="">
|
||||
|
||||
|
||||
<link rel="preload" href="/docs/assets/css/styles.ad3166d6.css" as="style"><link rel="preload" href="/docs/assets/js/app.ad3166d6.js" as="script"><link rel="preload" href="/docs/assets/js/2.142d04d2.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.83ce04b6.js"><link rel="prefetch" href="/docs/assets/js/3.2b6c987b.js"><link rel="prefetch" href="/docs/assets/js/4.ad90d74a.js"><link rel="prefetch" href="/docs/assets/js/5.36121818.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.ad3166d6.css">
|
||||
<link rel="preload" href="/docs/assets/css/styles.d51c815b.css" as="style"><link rel="preload" href="/docs/assets/js/app.d51c815b.js" as="script"><link rel="preload" href="/docs/assets/js/2.190ec46a.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.6babbc1d.js"><link rel="prefetch" href="/docs/assets/js/3.7646e76c.js"><link rel="prefetch" href="/docs/assets/js/4.08fbc0d9.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.d51c815b.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/develop.html" class="active sidebar-link">插件开发</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/docs/develop.html#插件的定义" class="sidebar-link">插件的定义</a></li><li class="sidebar-sub-header"><a href="/docs/develop.html#插件的安装" class="sidebar-link">插件的安装</a></li><li class="sidebar-sub-header"><a href="/docs/develop.html#开发订阅者插件" class="sidebar-link">开发订阅者插件</a></li><li class="sidebar-sub-header"><a href="/docs/develop.html#开发发布者插件" class="sidebar-link">开发发布者插件</a></li><li class="sidebar-sub-header"><a href="/docs/develop.html#开发钩子插件" class="sidebar-link">开发钩子插件</a></li></ul></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="插件开发"><a href="#插件开发" aria-hidden="true" class="header-anchor">#</a> 插件开发</h1><h2 id="插件的定义"><a href="#插件的定义" aria-hidden="true" class="header-anchor">#</a> 插件的定义</h2><p>所谓的插件,没有什么固定的规则,只需要完成<code>安装</code>操作即可。插件可以实现任意的功能扩展,最常见的是实现某种传输协议用来推流或者拉流</p><h2 id="插件的安装"><a href="#插件的安装" aria-hidden="true" class="header-anchor">#</a> 插件的安装</h2><p>下面是内置插件jessica的源码,代表了典型的插件安装</p><div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">package</span> jessica
|
||||
</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/develop.html" class="active sidebar-link">插件开发</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/docs/develop.html#插件的定义" class="sidebar-link">插件的定义</a></li><li class="sidebar-sub-header"><a href="/docs/develop.html#插件的安装" class="sidebar-link">插件的安装</a></li><li class="sidebar-sub-header"><a href="/docs/develop.html#开发订阅者插件" class="sidebar-link">开发订阅者插件</a></li><li class="sidebar-sub-header"><a href="/docs/develop.html#开发发布者插件" class="sidebar-link">开发发布者插件</a></li><li class="sidebar-sub-header"><a href="/docs/develop.html#开发钩子插件" class="sidebar-link">开发钩子插件</a></li></ul></li><li><a href="/docs/history.html" class="sidebar-link">更新日志</a></li><li><a href="/docs/plugins.html" class="sidebar-link">内置插件</a></li></ul></div><div class="page"><div class="content"><h1 id="插件开发"><a href="#插件开发" aria-hidden="true" class="header-anchor">#</a> 插件开发</h1><h2 id="插件的定义"><a href="#插件的定义" aria-hidden="true" class="header-anchor">#</a> 插件的定义</h2><p>所谓的插件,没有什么固定的规则,只需要完成<code>安装</code>操作即可。插件可以实现任意的功能扩展,最常见的是实现某种传输协议用来推流或者拉流</p><h2 id="插件的安装"><a href="#插件的安装" aria-hidden="true" class="header-anchor">#</a> 插件的安装</h2><p>下面是内置插件jessica的源码,代表了典型的插件安装</p><div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">package</span> jessica
|
||||
|
||||
<span class="token keyword">import</span> <span class="token punctuation">(</span>
|
||||
<span class="token punctuation">.</span> <span class="token string">"github.com/langhuihui/monibuca/monica"</span>
|
||||
@@ -161,13 +161,29 @@
|
||||
<span class="token punctuation">}</span>
|
||||
<span class="token punctuation">}</span>
|
||||
|
||||
</code></pre></div><p>正在该函数中会向源服务器建立tcp连接,然后发送特定命令表示需要拉流,当我们接收到源服务器的数据的时候,就调用PushVideo和PushAudio函数来广播音视频。</p><p>核心逻辑是调用InputStream的Publish以及PushVideo、PushAudio函数</p><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">
|
||||
</code></pre></div><p>正在该函数中会向源服务器建立tcp连接,然后发送特定命令表示需要拉流,当我们接收到源服务器的数据的时候,就调用PushVideo和PushAudio函数来广播音视频。</p><p>核心逻辑是调用InputStream的Publish以及PushVideo、PushAudio函数</p><h2 id="开发钩子插件"><a href="#开发钩子插件" aria-hidden="true" class="header-anchor">#</a> 开发钩子插件</h2><p>钩子插件就是在服务器的关键逻辑处插入的函数调用,方便扩展服务器的功能,比如对连接进行验证,或者触发一些特殊的发布者。
|
||||
目前提供的钩子包括</p><ul><li>当发布者开始发布时 <code>OnPublishHooks.AddHook(onPublish)</code>
|
||||
例如:</li></ul><div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">func</span> <span class="token function">onPublish</span><span class="token punctuation">(</span>r <span class="token operator">*</span>Room<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
||||
<span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> r<span class="token punctuation">.</span>Subscribers <span class="token punctuation">{</span>
|
||||
<span class="token keyword">if</span> err <span class="token operator">:=</span> <span class="token function">CheckSign</span><span class="token punctuation">(</span>v<span class="token punctuation">.</span>Sign<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
|
||||
v<span class="token punctuation">.</span><span class="token function">Cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
|
||||
<span class="token punctuation">}</span>
|
||||
<span class="token punctuation">}</span>
|
||||
<span class="token punctuation">}</span>
|
||||
</code></pre></div><p>此时可以访问房间里面的订阅者,对其进行验证。</p><ul><li>当有订阅者订阅了某个流时,<code>OnSubscribeHooks.AddHook(onSubscribe)</code>
|
||||
例如:</li></ul><div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">func</span> <span class="token function">onSubscribe</span><span class="token punctuation">(</span>s <span class="token operator">*</span>OutputStream<span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
||||
<span class="token keyword">if</span> s<span class="token punctuation">.</span>Publisher <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
|
||||
<span class="token keyword">go</span> <span class="token function">PullUpStream</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span>StreamPath<span class="token punctuation">)</span>
|
||||
<span class="token punctuation">}</span>
|
||||
<span class="token punctuation">}</span>
|
||||
|
||||
</code></pre></div><p>拉取源服务器的流</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/history.html">
|
||||
更新日志
|
||||
</a> →
|
||||
</span></p></div></div></div></div>
|
||||
<script src="/docs/assets/js/app.ad3166d6.js" defer></script><script src="/docs/assets/js/2.142d04d2.js" defer></script>
|
||||
<script src="/docs/assets/js/app.d51c815b.js" defer></script><script src="/docs/assets/js/2.190ec46a.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
14
dashboard/dist/docs/history.html
vendored
14
dashboard/dist/docs/history.html
vendored
@@ -7,20 +7,20 @@
|
||||
<meta name="description" content="">
|
||||
|
||||
|
||||
<link rel="preload" href="/docs/assets/css/styles.ad3166d6.css" as="style"><link rel="preload" href="/docs/assets/js/app.ad3166d6.js" as="script"><link rel="preload" href="/docs/assets/js/3.2b6c987b.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.83ce04b6.js"><link rel="prefetch" href="/docs/assets/js/2.142d04d2.js"><link rel="prefetch" href="/docs/assets/js/4.ad90d74a.js"><link rel="prefetch" href="/docs/assets/js/5.36121818.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.ad3166d6.css">
|
||||
<link rel="preload" href="/docs/assets/css/styles.d51c815b.css" as="style"><link rel="preload" href="/docs/assets/js/app.d51c815b.js" as="script"><link rel="preload" href="/docs/assets/js/3.7646e76c.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.6babbc1d.js"><link rel="prefetch" href="/docs/assets/js/2.190ec46a.js"><link rel="prefetch" href="/docs/assets/js/4.08fbc0d9.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.d51c815b.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/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="更新历史"><a href="#更新历史" aria-hidden="true" class="header-anchor">#</a> 更新历史</h1><ul><li>2020/1/27
|
||||
1.0完成</li></ul></div><div class="page-edit"><!----><!----></div><div class="page-nav"><p class="inner"><span class="prev">
|
||||
</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/develop.html" class="sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="active sidebar-link">更新日志</a></li><li><a href="/docs/plugins.html" class="sidebar-link">内置插件</a></li></ul></div><div class="page"><div class="content"><h1 id="更新历史"><a href="#更新历史" aria-hidden="true" class="header-anchor">#</a> 更新历史</h1><ul><li>2020/1/27
|
||||
初步完成</li></ul></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><span class="next"><a href="/docs/plugins.html">
|
||||
内置插件
|
||||
</a> →
|
||||
</span></p></div></div></div></div>
|
||||
<script src="/docs/assets/js/app.ad3166d6.js" defer></script><script src="/docs/assets/js/3.2b6c987b.js" defer></script>
|
||||
<script src="/docs/assets/js/app.d51c815b.js" defer></script><script src="/docs/assets/js/3.7646e76c.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
8
dashboard/dist/docs/index.html
vendored
8
dashboard/dist/docs/index.html
vendored
@@ -7,13 +7,13 @@
|
||||
<meta name="description" content="">
|
||||
|
||||
|
||||
<link rel="preload" href="/docs/assets/css/styles.ad3166d6.css" as="style"><link rel="preload" href="/docs/assets/js/app.ad3166d6.js" as="script"><link rel="preload" href="/docs/assets/js/1.83ce04b6.js" as="script"><link rel="prefetch" href="/docs/assets/js/2.142d04d2.js"><link rel="prefetch" href="/docs/assets/js/3.2b6c987b.js"><link rel="prefetch" href="/docs/assets/js/4.ad90d74a.js"><link rel="prefetch" href="/docs/assets/js/5.36121818.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.ad3166d6.css">
|
||||
<link rel="preload" href="/docs/assets/css/styles.d51c815b.css" as="style"><link rel="preload" href="/docs/assets/js/app.d51c815b.js" as="script"><link rel="preload" href="/docs/assets/js/1.6babbc1d.js" as="script"><link rel="prefetch" href="/docs/assets/js/2.190ec46a.js"><link rel="prefetch" href="/docs/assets/js/3.7646e76c.js"><link rel="prefetch" href="/docs/assets/js/4.08fbc0d9.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.d51c815b.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><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/docs/#介绍" class="sidebar-link">介绍</a></li><li class="sidebar-sub-header"><a href="/docs/#启动" class="sidebar-link">启动</a></li><li class="sidebar-sub-header"><a href="/docs/#配置" class="sidebar-link">配置</a></li></ul></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><h2 id="介绍"><a href="#介绍" aria-hidden="true" class="header-anchor">#</a> 介绍</h2><p>Monibuca 是一个开源的流媒体服务器开发框架,适用于快速定制化开发流媒体服务器,可以对接CDN厂商,作为回源服务器,也可以自己搭建集群部署环境。
|
||||
</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><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/docs/#介绍" class="sidebar-link">介绍</a></li><li class="sidebar-sub-header"><a href="/docs/#启动" class="sidebar-link">启动</a></li><li class="sidebar-sub-header"><a href="/docs/#配置" class="sidebar-link">配置</a></li></ul></li><li><a href="/docs/develop.html" class="sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="sidebar-link">更新日志</a></li><li><a href="/docs/plugins.html" class="sidebar-link">内置插件</a></li></ul></div><div class="page"><div class="content"><h1 id="monibuca快速起步"><a href="#monibuca快速起步" aria-hidden="true" class="header-anchor">#</a> Monibuca快速起步</h1><h2 id="介绍"><a href="#介绍" aria-hidden="true" class="header-anchor">#</a> 介绍</h2><p>Monibuca 是一个开源的流媒体服务器开发框架,适用于快速定制化开发流媒体服务器,可以对接CDN厂商,作为回源服务器,也可以自己搭建集群部署环境。
|
||||
丰富的内置插件提供了流媒体服务器的常见功能,例如rtmp server、http-flv、视频录制、QoS等。除此以外还内置了后台web界面,方便观察服务器运行的状态。
|
||||
也可以自己开发后台管理界面,通过api方式获取服务器的运行信息。
|
||||
Monibuca 提供了可供定制化开发的插件机制,可以任意扩展其功能。</p><h2 id="启动"><a href="#启动" aria-hidden="true" class="header-anchor">#</a> 启动</h2><p>启用所有内置插件</p><div class="language-go extra-class"><pre class="language-go"><code><span class="token keyword">package</span> main
|
||||
@@ -49,6 +49,6 @@ Monibuca 提供了可供定制化开发的插件机制,可以任意扩展其
|
||||
插件开发
|
||||
</a> →
|
||||
</span></p></div></div></div></div>
|
||||
<script src="/docs/assets/js/app.ad3166d6.js" defer></script><script src="/docs/assets/js/1.83ce04b6.js" defer></script>
|
||||
<script src="/docs/assets/js/app.d51c815b.js" defer></script><script src="/docs/assets/js/1.6babbc1d.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
44
dashboard/dist/docs/plugins.html
vendored
Normal file
44
dashboard/dist/docs/plugins.html
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<!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.d51c815b.css" as="style"><link rel="preload" href="/docs/assets/js/app.d51c815b.js" as="script"><link rel="preload" href="/docs/assets/js/4.08fbc0d9.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.6babbc1d.js"><link rel="prefetch" href="/docs/assets/js/2.190ec46a.js"><link rel="prefetch" href="/docs/assets/js/3.7646e76c.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.d51c815b.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/develop.html" class="sidebar-link">插件开发</a></li><li><a href="/docs/history.html" class="sidebar-link">更新日志</a></li><li><a href="/docs/plugins.html" class="active sidebar-link">内置插件</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/docs/plugins.html#jessica插件" class="sidebar-link">Jessica插件</a></li><li class="sidebar-sub-header"><a href="/docs/plugins.html#rtmp插件" class="sidebar-link">Rtmp插件</a></li><li class="sidebar-sub-header"><a href="/docs/plugins.html#recordflv插件" class="sidebar-link">RecordFlv插件</a></li><li class="sidebar-sub-header"><a href="/docs/plugins.html#http-flv插件" class="sidebar-link">Http-Flv插件</a></li><li class="sidebar-sub-header"><a href="/docs/plugins.html#cluster插件" class="sidebar-link">Cluster插件</a></li><li class="sidebar-sub-header"><a href="/docs/plugins.html#hls插件" class="sidebar-link">HLS插件</a></li><li class="sidebar-sub-header"><a href="/docs/plugins.html#网关插件" class="sidebar-link">网关插件</a></li><li class="sidebar-sub-header"><a href="/docs/plugins.html#校验插件" class="sidebar-link">校验插件</a></li></ul></li></ul></div><div class="page"><div class="content"><h1 id="内置插件介绍"><a href="#内置插件介绍" aria-hidden="true" class="header-anchor">#</a> 内置插件介绍</h1><p>内置插件为Monibuca提供了许多基础功能,当然你完全可以不采用内置插件,而改用自己开发的插件,也丝毫不会影响您使用Monibuca。</p><h2 id="jessica插件"><a href="#jessica插件" aria-hidden="true" class="header-anchor">#</a> Jessica插件</h2><blockquote><p>该插件源码位于plugins/jessica下</p></blockquote><p>该插件为基于WebSocket协议传输音视频的订阅者,音视频数据以裸数据的形式进行传输,我们需要Jessibuca播放器来进行播放
|
||||
Jessibua播放器已内置于源码中,该播放器通过js解码H264/H265并用canvas进行渲染,可以运行在几乎所有的终端浏览器上面。
|
||||
在Monibuca的Web界面中预览功能就是使用的Jessibuca播放器。</p><h3 id="配置"><a href="#配置" aria-hidden="true" class="header-anchor">#</a> 配置</h3><p>目前仅有的配置是监听的端口号</p><div class="language-toml extra-class"><pre class="language-toml"><code><span class="token punctuation">[</span><span class="token table class-name">Plugins.Jessica</span><span class="token punctuation">]</span>
|
||||
<span class="token key property">ListenAddr</span> <span class="token punctuation">=</span> <span class="token string">":8080"</span>
|
||||
</code></pre></div><h3 id="flv格式支持"><a href="#flv格式支持" aria-hidden="true" class="header-anchor">#</a> Flv格式支持</h3><p>Jessica以及Jessibuca也支持采用WebSocket中传输Flv格式的方式进行通讯,目前有部分CDN厂商已经支持这种方式进行传输。</p><blockquote><p>私有协议以及Flv格式的判断是通过URL后缀是否带有.flv来进行判断</p></blockquote><h2 id="rtmp插件"><a href="#rtmp插件" aria-hidden="true" class="header-anchor">#</a> Rtmp插件</h2><blockquote><p>该插件源码位于plugins/rtmp下</p></blockquote><p>实现了基本的rtmp传输协议,包括接收来自OBS、ffmpeg等软件的推流,以及来在Flash Player播放器的拉流。</p><h3 id="配置-2"><a href="#配置-2" aria-hidden="true" class="header-anchor">#</a> 配置</h3><p>目前仅有的配置是监听的端口号</p><div class="language-toml extra-class"><pre class="language-toml"><code><span class="token punctuation">[</span><span class="token table class-name">Plugins.RTMP</span><span class="token punctuation">]</span>
|
||||
<span class="token key property">ListenAddr</span> <span class="token punctuation">=</span> <span class="token string">":1935"</span>
|
||||
</code></pre></div><h2 id="recordflv插件"><a href="#recordflv插件" aria-hidden="true" class="header-anchor">#</a> RecordFlv插件</h2><blockquote><p>该插件源码位于plugins/record下</p></blockquote><p>实现了录制Flv文件的功能,并且支持再次使用录制好的Flv文件作为发布者进行发布。在Monibuca的web界面的控制台中提供了对房间进行录制的操作按钮,以及列出所有已经录制的文件的界面。</p><h3 id="配置-3"><a href="#配置-3" aria-hidden="true" class="header-anchor">#</a> 配置</h3><p>配置中的Path 表示要保存的Flv文件的根路径,可以使用相对路径或者绝对路径</p><div class="language-toml extra-class"><pre class="language-toml"><code><span class="token punctuation">[</span><span class="token table class-name">Plugins.RecordFlv</span><span class="token punctuation">]</span>
|
||||
<span class="token key property">Path</span><span class="token punctuation">=</span><span class="token string">"./resource"</span>
|
||||
</code></pre></div><h2 id="http-flv插件"><a href="#http-flv插件" aria-hidden="true" class="header-anchor">#</a> Http-Flv插件</h2><blockquote><p>该插件位于plugins/HDL下</p></blockquote><p>实现了http-flv格式的拉流功能,方便对接CDN厂商</p><h3 id="配置-4"><a href="#配置-4" aria-hidden="true" class="header-anchor">#</a> 配置</h3><p>目前仅有的配置是监听的端口号</p><div class="language-toml extra-class"><pre class="language-toml"><code><span class="token punctuation">[</span><span class="token table class-name">Plugins.HDL</span><span class="token punctuation">]</span>
|
||||
<span class="token key property">ListenAddr</span> <span class="token punctuation">=</span> <span class="token string">":2020"</span>
|
||||
</code></pre></div><h2 id="cluster插件"><a href="#cluster插件" aria-hidden="true" class="header-anchor">#</a> Cluster插件</h2><blockquote><p>该插件源码位于plugins/cluster下</p></blockquote><p>实现了基本的集群功能,里面包含一对发布者和订阅者,分别在主从服务器中启用,进行连接。
|
||||
起基本原理就是,在主服务器启动端口监听,从服务器收到播放请求时,如果从服务器没有对应的发布者,则向主服务器发起请求,主服务器收到来自从服务器的请求时,将该请求作为一个订阅者。从服务器则把tcp连接作为发布者,实现视频流的传递过程。</p><h3 id="配置-5"><a href="#配置-5" aria-hidden="true" class="header-anchor">#</a> 配置</h3><p>主服务器的配置是ListenAddr,用来监听从服务器的请求。
|
||||
从服务器的配置是Master,表示主服务器的地址。
|
||||
当然服务器可以既是主也是从,即充当中转站。</p><div class="language-toml extra-class"><pre class="language-toml"><code><span class="token punctuation">[</span><span class="token table class-name">Plugins.Cluster</span><span class="token punctuation">]</span>
|
||||
<span class="token key property">Master</span> <span class="token punctuation">=</span> <span class="token string">"localhost:2019"</span>
|
||||
<span class="token key property">ListenAddr</span> <span class="token punctuation">=</span> <span class="token string">":2019"</span>
|
||||
</code></pre></div><h2 id="hls插件"><a href="#hls插件" aria-hidden="true" class="header-anchor">#</a> HLS插件</h2><blockquote><p>该插件源码位于plugins/HLS下</p></blockquote><p>该插件的作用是请求M3u8文件进行解码,最终将TS视频流转码成裸的视频流进行发布。
|
||||
注意:该插件目前并没有实现生成HLS的功能。</p><h2 id="网关插件"><a href="#网关插件" aria-hidden="true" class="header-anchor">#</a> 网关插件</h2><blockquote><p>该插件位于plugins/gateway下</p></blockquote><p>该插件是为web控制台界面提供api,用来采集服务器的信息。</p><h3 id="配置-6"><a href="#配置-6" aria-hidden="true" class="header-anchor">#</a> 配置</h3><p>目前仅有的配置是监听的端口号</p><div class="language-toml extra-class"><pre class="language-toml"><code><span class="token punctuation">[</span><span class="token table class-name">Plugins.GateWay</span><span class="token punctuation">]</span>
|
||||
<span class="token key property">ListenAddr</span> <span class="token punctuation">=</span> <span class="token string">":80"</span>
|
||||
</code></pre></div><p>如果80端口有其他用途,可以换成别的端口,比如有nginx反向代理。</p><h2 id="校验插件"><a href="#校验插件" aria-hidden="true" class="header-anchor">#</a> 校验插件</h2><blockquote><p>该插件位于plugins/auth下</p></blockquote><p>该插件提供了基本的验证功能,其原理是
|
||||
订阅流提供一个签名,签名只可以使用一次,把签名进行AES CBC 解密,如果得到的解密字符串的前面部分就是和Key相同则通过验证。</p><h3 id="配置-7"><a href="#配置-7" aria-hidden="true" class="header-anchor">#</a> 配置</h3><p>Key代表用来加密的Key</p><div class="language-toml extra-class"><pre class="language-toml"><code><span class="token punctuation">[</span><span class="token table class-name">Plugins.Auth</span><span class="token punctuation">]</span>
|
||||
<span class="token key property">Key</span><span class="token punctuation">=</span><span class="token string">"www.monibuca.com"</span>
|
||||
</code></pre></div></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.d51c815b.js" defer></script><script src="/docs/assets/js/4.08fbc0d9.js" defer></script>
|
||||
</body>
|
||||
</html>
|
19
dashboard/dist/docs/plugins/index.html
vendored
19
dashboard/dist/docs/plugins/index.html
vendored
@@ -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.ad3166d6.css" as="style"><link rel="preload" href="/docs/assets/js/app.ad3166d6.js" as="script"><link rel="preload" href="/docs/assets/js/4.ad90d74a.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.83ce04b6.js"><link rel="prefetch" href="/docs/assets/js/2.142d04d2.js"><link rel="prefetch" href="/docs/assets/js/3.2b6c987b.js"><link rel="prefetch" href="/docs/assets/js/5.36121818.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.ad3166d6.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/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.ad3166d6.js" defer></script><script src="/docs/assets/js/4.ad90d74a.js" defer></script>
|
||||
</body>
|
||||
</html>
|
26
dashboard/dist/docs/plugins/jessica.html
vendored
26
dashboard/dist/docs/plugins/jessica.html
vendored
@@ -1,26 +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.ad3166d6.css" as="style"><link rel="preload" href="/docs/assets/js/app.ad3166d6.js" as="script"><link rel="preload" href="/docs/assets/js/5.36121818.js" as="script"><link rel="prefetch" href="/docs/assets/js/1.83ce04b6.js"><link rel="prefetch" href="/docs/assets/js/2.142d04d2.js"><link rel="prefetch" href="/docs/assets/js/3.2b6c987b.js"><link rel="prefetch" href="/docs/assets/js/4.ad90d74a.js">
|
||||
<link rel="stylesheet" href="/docs/assets/css/styles.ad3166d6.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/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><p>该插件为基于WebSocket协议传输音视频的订阅者,音视频数据以裸数据的形式进行传输,我们需要Jessibuca播放器来进行播放
|
||||
Jessibua播放器已内置于源码中,该播放器通过js解码H264并用canvas进行渲染,可以运行在几乎所有的终端浏览器上面。
|
||||
在Monibuca的Web界面中预览功能就是使用的Jessibuca播放器。</p><h2 id="配置"><a href="#配置" aria-hidden="true" class="header-anchor">#</a> 配置</h2><p>目前仅有的配置是监听的端口号</p><div class="language-toml extra-class"><pre class="language-toml"><code><span class="token punctuation">[</span><span class="token table class-name">Plugins.Jessica</span><span class="token punctuation">]</span>
|
||||
<span class="token key property">ListenAddr</span> <span class="token punctuation">=</span> <span class="token string">":8080"</span>
|
||||
</code></pre></div></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.ad3166d6.js" defer></script><script src="/docs/assets/js/5.36121818.js" defer></script>
|
||||
</body>
|
||||
</html>
|
42
dashboard/dist/docs/service-worker.js
vendored
42
dashboard/dist/docs/service-worker.js
vendored
@@ -21,10 +21,10 @@ importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox
|
||||
self.__precacheManifest = [
|
||||
{
|
||||
"url": "404.html",
|
||||
"revision": "a3d9e915fd09958cab2da343fcad58b0"
|
||||
"revision": "75925fb89348802ecd737cdad2d5d801"
|
||||
},
|
||||
{
|
||||
"url": "assets/css/styles.ad3166d6.css",
|
||||
"url": "assets/css/styles.d51c815b.css",
|
||||
"revision": "4a6b650244e5b709f84a81ad0565b485"
|
||||
},
|
||||
{
|
||||
@@ -32,48 +32,40 @@ self.__precacheManifest = [
|
||||
"revision": "83621669651b9a3d4bf64d1a670ad856"
|
||||
},
|
||||
{
|
||||
"url": "assets/js/1.83ce04b6.js",
|
||||
"revision": "3cabebb5c79c8280aee24ac6e4545650"
|
||||
"url": "assets/js/1.6babbc1d.js",
|
||||
"revision": "0142763a4e8630af56b66f5fa8320c5c"
|
||||
},
|
||||
{
|
||||
"url": "assets/js/2.142d04d2.js",
|
||||
"revision": "027f4f643a10a465692035fe692cd94f"
|
||||
"url": "assets/js/2.190ec46a.js",
|
||||
"revision": "8dceb01ded85f36cc30e0e18371fe5d4"
|
||||
},
|
||||
{
|
||||
"url": "assets/js/3.2b6c987b.js",
|
||||
"revision": "27a988ab518e3f04db65045269d55841"
|
||||
"url": "assets/js/3.7646e76c.js",
|
||||
"revision": "378c41587710afd31466293c83a6c738"
|
||||
},
|
||||
{
|
||||
"url": "assets/js/4.ad90d74a.js",
|
||||
"revision": "dcbfc54b67e9e6e33dbb2a303e842bdc"
|
||||
"url": "assets/js/4.08fbc0d9.js",
|
||||
"revision": "0ea387538f5b25ef46237e3dcc1c1694"
|
||||
},
|
||||
{
|
||||
"url": "assets/js/5.36121818.js",
|
||||
"revision": "550520f0f388530dfbc4cc40a3dee264"
|
||||
},
|
||||
{
|
||||
"url": "assets/js/app.ad3166d6.js",
|
||||
"revision": "fdeb68e4b7b08c2c7bdcaf7462f34b16"
|
||||
"url": "assets/js/app.d51c815b.js",
|
||||
"revision": "467373b055c3575c2082a1b9ab1769fd"
|
||||
},
|
||||
{
|
||||
"url": "develop.html",
|
||||
"revision": "f7461297bdc3ce303d517f0fc6dedd78"
|
||||
"revision": "92b13eb27581e4dc7008cf4205e5c215"
|
||||
},
|
||||
{
|
||||
"url": "history.html",
|
||||
"revision": "c4ba2b13246f7a9e0039852fe381de6e"
|
||||
"revision": "ef202ac3bf63e1bdef479a05d49d5a8d"
|
||||
},
|
||||
{
|
||||
"url": "index.html",
|
||||
"revision": "3371a6066d0c2229a521d0659f25b174"
|
||||
"revision": "306f1a0a8ceadb0c85dae2342f0bd637"
|
||||
},
|
||||
{
|
||||
"url": "plugins/index.html",
|
||||
"revision": "511b5cef98368f5f0ab35e667766e367"
|
||||
},
|
||||
{
|
||||
"url": "plugins/jessica.html",
|
||||
"revision": "65ca2965d5d514441d64b4d5e38cd696"
|
||||
"url": "plugins.html",
|
||||
"revision": "ac2ce38679fb75f05e8e501f35d161bd"
|
||||
}
|
||||
].concat(self.__precacheManifest || []);
|
||||
workbox.precaching.suppressWarnings();
|
||||
|
2
dashboard/dist/index.html
vendored
2
dashboard/dist/index.html
vendored
@@ -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.b4fc79e8.css rel=preload as=style><link href=/css/chunk-vendors.22ebf426.css rel=preload as=style><link href=/js/app.9cc902b4.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.b4fc79e8.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.9cc902b4.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.ea4656d8.css rel=preload as=style><link href=/css/chunk-vendors.22ebf426.css rel=preload as=style><link href=/js/app.af5e5ef3.js rel=preload as=script><link href=/js/chunk-vendors.ebc28a73.js rel=preload as=script><link href=/css/chunk-vendors.22ebf426.css rel=stylesheet><link href=/css/app.ea4656d8.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.ebc28a73.js></script><script src=/js/app.af5e5ef3.js></script></body></html>
|
2
dashboard/dist/js/app.9cc902b4.js
vendored
2
dashboard/dist/js/app.9cc902b4.js
vendored
File diff suppressed because one or more lines are too long
1
dashboard/dist/js/app.9cc902b4.js.map
vendored
1
dashboard/dist/js/app.9cc902b4.js.map
vendored
File diff suppressed because one or more lines are too long
2
dashboard/dist/js/app.af5e5ef3.js
vendored
Normal file
2
dashboard/dist/js/app.af5e5ef3.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dashboard/dist/js/app.af5e5ef3.js.map
vendored
Normal file
1
dashboard/dist/js/app.af5e5ef3.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
44
dashboard/dist/js/chunk-vendors.ae8ac63d.js
vendored
44
dashboard/dist/js/chunk-vendors.ae8ac63d.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
52
dashboard/dist/js/chunk-vendors.ebc28a73.js
vendored
Normal file
52
dashboard/dist/js/chunk-vendors.ebc28a73.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dashboard/dist/js/chunk-vendors.ebc28a73.js.map
vendored
Normal file
1
dashboard/dist/js/chunk-vendors.ebc28a73.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -5,14 +5,8 @@ module.exports = {
|
||||
sidebar: [
|
||||
['/', '起步'],
|
||||
['/develop', '插件开发'],
|
||||
[ '/history', '更新日志' ],
|
||||
{
|
||||
title: '内置插件',
|
||||
path: '/plugins/',
|
||||
children: [
|
||||
'/plugins/jessica'
|
||||
]
|
||||
},
|
||||
['/history', '更新日志'],
|
||||
['/plugins', '内置插件']
|
||||
]
|
||||
},
|
||||
title: 'Monibuca',
|
||||
|
@@ -176,3 +176,29 @@ func PullUpStream(streamPath string) {
|
||||
|
||||
## 开发钩子插件
|
||||
|
||||
钩子插件就是在服务器的关键逻辑处插入的函数调用,方便扩展服务器的功能,比如对连接进行验证,或者触发一些特殊的发布者。
|
||||
目前提供的钩子包括
|
||||
- 当发布者开始发布时 `OnPublishHooks.AddHook(onPublish)`
|
||||
例如:
|
||||
```go
|
||||
func onPublish(r *Room) {
|
||||
for _, v := range r.Subscribers {
|
||||
if err := CheckSign(v.Sign); err != nil {
|
||||
v.Cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
此时可以访问房间里面的订阅者,对其进行验证。
|
||||
- 当有订阅者订阅了某个流时,`OnSubscribeHooks.AddHook(onSubscribe)`
|
||||
例如:
|
||||
```go
|
||||
func onSubscribe(s *OutputStream) {
|
||||
if s.Publisher == nil {
|
||||
go PullUpStream(s.StreamPath)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
拉取源服务器的流
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# 更新历史
|
||||
|
||||
- 2020/1/27
|
||||
1.0完成
|
||||
初步完成
|
104
dashboard/docs/plugins.md
Normal file
104
dashboard/docs/plugins.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# 内置插件介绍
|
||||
内置插件为Monibuca提供了许多基础功能,当然你完全可以不采用内置插件,而改用自己开发的插件,也丝毫不会影响您使用Monibuca。
|
||||
## Jessica插件
|
||||
> 该插件源码位于plugins/jessica下
|
||||
|
||||
该插件为基于WebSocket协议传输音视频的订阅者,音视频数据以裸数据的形式进行传输,我们需要Jessibuca播放器来进行播放
|
||||
Jessibua播放器已内置于源码中,该播放器通过js解码H264/H265并用canvas进行渲染,可以运行在几乎所有的终端浏览器上面。
|
||||
在Monibuca的Web界面中预览功能就是使用的Jessibuca播放器。
|
||||
### 配置
|
||||
目前仅有的配置是监听的端口号
|
||||
```toml
|
||||
[Plugins.Jessica]
|
||||
ListenAddr = ":8080"
|
||||
```
|
||||
### Flv格式支持
|
||||
Jessica以及Jessibuca也支持采用WebSocket中传输Flv格式的方式进行通讯,目前有部分CDN厂商已经支持这种方式进行传输。
|
||||
>私有协议以及Flv格式的判断是通过URL后缀是否带有.flv来进行判断
|
||||
|
||||
## Rtmp插件
|
||||
> 该插件源码位于plugins/rtmp下
|
||||
|
||||
实现了基本的rtmp传输协议,包括接收来自OBS、ffmpeg等软件的推流,以及来在Flash Player播放器的拉流。
|
||||
|
||||
### 配置
|
||||
目前仅有的配置是监听的端口号
|
||||
```toml
|
||||
[Plugins.RTMP]
|
||||
ListenAddr = ":1935"
|
||||
```
|
||||
|
||||
## RecordFlv插件
|
||||
> 该插件源码位于plugins/record下
|
||||
|
||||
实现了录制Flv文件的功能,并且支持再次使用录制好的Flv文件作为发布者进行发布。在Monibuca的web界面的控制台中提供了对房间进行录制的操作按钮,以及列出所有已经录制的文件的界面。
|
||||
|
||||
### 配置
|
||||
配置中的Path 表示要保存的Flv文件的根路径,可以使用相对路径或者绝对路径
|
||||
```toml
|
||||
[Plugins.RecordFlv]
|
||||
Path="./resource"
|
||||
```
|
||||
|
||||
## Http-Flv插件
|
||||
> 该插件位于plugins/HDL下
|
||||
|
||||
实现了http-flv格式的拉流功能,方便对接CDN厂商
|
||||
|
||||
### 配置
|
||||
目前仅有的配置是监听的端口号
|
||||
```toml
|
||||
[Plugins.HDL]
|
||||
ListenAddr = ":2020"
|
||||
```
|
||||
|
||||
## Cluster插件
|
||||
> 该插件源码位于plugins/cluster下
|
||||
|
||||
实现了基本的集群功能,里面包含一对发布者和订阅者,分别在主从服务器中启用,进行连接。
|
||||
起基本原理就是,在主服务器启动端口监听,从服务器收到播放请求时,如果从服务器没有对应的发布者,则向主服务器发起请求,主服务器收到来自从服务器的请求时,将该请求作为一个订阅者。从服务器则把tcp连接作为发布者,实现视频流的传递过程。
|
||||
|
||||
### 配置
|
||||
|
||||
主服务器的配置是ListenAddr,用来监听从服务器的请求。
|
||||
从服务器的配置是Master,表示主服务器的地址。
|
||||
当然服务器可以既是主也是从,即充当中转站。
|
||||
|
||||
```toml
|
||||
[Plugins.Cluster]
|
||||
Master = "localhost:2019"
|
||||
ListenAddr = ":2019"
|
||||
```
|
||||
|
||||
## HLS插件
|
||||
> 该插件源码位于plugins/HLS下
|
||||
|
||||
该插件的作用是请求M3u8文件进行解码,最终将TS视频流转码成裸的视频流进行发布。
|
||||
注意:该插件目前并没有实现生成HLS的功能。
|
||||
|
||||
## 网关插件
|
||||
> 该插件位于plugins/gateway下
|
||||
|
||||
该插件是为web控制台界面提供api,用来采集服务器的信息。
|
||||
|
||||
### 配置
|
||||
目前仅有的配置是监听的端口号
|
||||
|
||||
```toml
|
||||
[Plugins.GateWay]
|
||||
ListenAddr = ":80"
|
||||
```
|
||||
如果80端口有其他用途,可以换成别的端口,比如有nginx反向代理。
|
||||
|
||||
## 校验插件
|
||||
> 该插件位于plugins/auth下
|
||||
|
||||
该插件提供了基本的验证功能,其原理是
|
||||
订阅流提供一个签名,签名只可以使用一次,把签名进行AES CBC 解密,如果得到的解密字符串的前面部分就是和Key相同则通过验证。
|
||||
|
||||
### 配置
|
||||
Key代表用来加密的Key
|
||||
```toml
|
||||
[Plugins.Auth]
|
||||
Key="www.monibuca.com"
|
||||
```
|
@@ -1,10 +0,0 @@
|
||||
# Jessica
|
||||
该插件为基于WebSocket协议传输音视频的订阅者,音视频数据以裸数据的形式进行传输,我们需要Jessibuca播放器来进行播放
|
||||
Jessibua播放器已内置于源码中,该播放器通过js解码H264并用canvas进行渲染,可以运行在几乎所有的终端浏览器上面。
|
||||
在Monibuca的Web界面中预览功能就是使用的Jessibuca播放器。
|
||||
## 配置
|
||||
目前仅有的配置是监听的端口号
|
||||
```toml
|
||||
[Plugins.Jessica]
|
||||
ListenAddr = ":8080"
|
||||
```
|
360
dashboard/package-lock.json
generated
360
dashboard/package-lock.json
generated
@@ -123,6 +123,44 @@
|
||||
"tslib": "1.10.0"
|
||||
}
|
||||
},
|
||||
"@antv/g6": {
|
||||
"version": "3.2.9",
|
||||
"resolved": "https://registry.npm.taobao.org/@antv/g6/download/@antv/g6-3.2.9.tgz",
|
||||
"integrity": "sha1-V4k4YKrE3iZUTz1dSoG4jWXVK9k=",
|
||||
"requires": {
|
||||
"@antv/g": "3.4.7",
|
||||
"@antv/hierarchy": "0.6.1",
|
||||
"@antv/util": "1.3.1",
|
||||
"d3": "5.15.0",
|
||||
"d3-force": "2.0.1",
|
||||
"d3-sankey": "0.12.3",
|
||||
"dagre": "0.8.5",
|
||||
"numeric": "1.2.6",
|
||||
"numericjs": "1.2.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/g": {
|
||||
"version": "3.4.7",
|
||||
"resolved": "https://registry.npm.taobao.org/@antv/g/download/@antv/g-3.4.7.tgz",
|
||||
"integrity": "sha1-1CCM07f4XVEvp19l0YXIoFoYHuU=",
|
||||
"requires": {
|
||||
"@antv/gl-matrix": "2.7.1",
|
||||
"@antv/util": "1.3.1",
|
||||
"d3-ease": "1.0.6",
|
||||
"d3-interpolate": "1.1.6",
|
||||
"d3-timer": "1.0.10"
|
||||
}
|
||||
},
|
||||
"@antv/util": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npm.taobao.org/@antv/util/download/@antv/util-1.3.1.tgz?cache=0&sync_timestamp=1572438102677&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40antv%2Futil%2Fdownload%2F%40antv%2Futil-1.3.1.tgz",
|
||||
"integrity": "sha1-MKNLIB/5Em7A1YxyyBZqnD5kTM0=",
|
||||
"requires": {
|
||||
"@antv/gl-matrix": "2.7.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@antv/gl-matrix": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npm.taobao.org/@antv/gl-matrix/download/@antv/gl-matrix-2.7.1.tgz",
|
||||
@@ -141,6 +179,24 @@
|
||||
"tslib": "1.10.0"
|
||||
}
|
||||
},
|
||||
"@antv/hierarchy": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npm.taobao.org/@antv/hierarchy/download/@antv/hierarchy-0.6.1.tgz",
|
||||
"integrity": "sha1-2oH6FUTNTj/17D+WAfv4FC9h7yw=",
|
||||
"requires": {
|
||||
"@antv/util": "1.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/util": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npm.taobao.org/@antv/util/download/@antv/util-1.3.1.tgz?cache=0&sync_timestamp=1572438102677&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40antv%2Futil%2Fdownload%2F%40antv%2Futil-1.3.1.tgz",
|
||||
"integrity": "sha1-MKNLIB/5Em7A1YxyyBZqnD5kTM0=",
|
||||
"requires": {
|
||||
"@antv/gl-matrix": "2.7.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@antv/matrix-util": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npm.taobao.org/@antv/matrix-util/download/@antv/matrix-util-2.0.4.tgz",
|
||||
@@ -4409,8 +4465,7 @@
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npm.taobao.org/commander/download/commander-2.20.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.20.3.tgz",
|
||||
"integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=",
|
||||
"dev": true
|
||||
"integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM="
|
||||
},
|
||||
"common-tags": {
|
||||
"version": "1.8.0",
|
||||
@@ -5130,16 +5185,171 @@
|
||||
"type": "1.2.0"
|
||||
}
|
||||
},
|
||||
"d3": {
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npm.taobao.org/d3/download/d3-5.15.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3%2Fdownload%2Fd3-5.15.0.tgz",
|
||||
"integrity": "sha1-/9RJWOajy4pZqEQpxFQpuLylZ3o=",
|
||||
"requires": {
|
||||
"d3-array": "1.2.4",
|
||||
"d3-axis": "1.0.12",
|
||||
"d3-brush": "1.1.5",
|
||||
"d3-chord": "1.0.6",
|
||||
"d3-collection": "1.0.7",
|
||||
"d3-color": "1.4.0",
|
||||
"d3-contour": "1.3.2",
|
||||
"d3-dispatch": "1.0.6",
|
||||
"d3-drag": "1.2.5",
|
||||
"d3-dsv": "1.2.0",
|
||||
"d3-ease": "1.0.6",
|
||||
"d3-fetch": "1.1.2",
|
||||
"d3-force": "1.2.1",
|
||||
"d3-format": "1.4.3",
|
||||
"d3-geo": "1.11.9",
|
||||
"d3-hierarchy": "1.1.9",
|
||||
"d3-interpolate": "1.1.6",
|
||||
"d3-path": "1.0.9",
|
||||
"d3-polygon": "1.0.6",
|
||||
"d3-quadtree": "1.0.7",
|
||||
"d3-random": "1.1.2",
|
||||
"d3-scale": "2.2.2",
|
||||
"d3-scale-chromatic": "1.5.0",
|
||||
"d3-selection": "1.4.1",
|
||||
"d3-shape": "1.3.7",
|
||||
"d3-time": "1.1.0",
|
||||
"d3-time-format": "2.2.3",
|
||||
"d3-timer": "1.0.10",
|
||||
"d3-transition": "1.3.2",
|
||||
"d3-voronoi": "1.1.4",
|
||||
"d3-zoom": "1.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-force": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-force/download/d3-force-1.2.1.tgz",
|
||||
"integrity": "sha1-/Sml0f8YHJ5/BmnkvXK9sOkU7As=",
|
||||
"requires": {
|
||||
"d3-collection": "1.0.7",
|
||||
"d3-dispatch": "1.0.6",
|
||||
"d3-quadtree": "1.0.7",
|
||||
"d3-timer": "1.0.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"d3-array": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-array/download/d3-array-1.2.4.tgz",
|
||||
"integrity": "sha1-Y1zk1e6nWfb2BYY9vPww7cc39x8="
|
||||
},
|
||||
"d3-axis": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-axis/download/d3-axis-1.0.12.tgz",
|
||||
"integrity": "sha1-zfILohDPu0N5WvM3Vohvs2ONqsk="
|
||||
},
|
||||
"d3-brush": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-brush/download/d3-brush-1.1.5.tgz",
|
||||
"integrity": "sha1-BmuOhNF7GSmGAwRGyXwPun4brNw=",
|
||||
"requires": {
|
||||
"d3-dispatch": "1.0.6",
|
||||
"d3-drag": "1.2.5",
|
||||
"d3-interpolate": "1.1.6",
|
||||
"d3-selection": "1.4.1",
|
||||
"d3-transition": "1.3.2"
|
||||
}
|
||||
},
|
||||
"d3-chord": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-chord/download/d3-chord-1.0.6.tgz?cache=0&sync_timestamp=1575418695102&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-chord%2Fdownload%2Fd3-chord-1.0.6.tgz",
|
||||
"integrity": "sha1-MJFX4/LbLHUvAoD+3TXyBnzLsV8=",
|
||||
"requires": {
|
||||
"d3-array": "1.2.4",
|
||||
"d3-path": "1.0.9"
|
||||
}
|
||||
},
|
||||
"d3-collection": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-collection/download/d3-collection-1.0.7.tgz",
|
||||
"integrity": "sha1-NJvSqpl32wcQkcExRNXk8WtbMQ4="
|
||||
},
|
||||
"d3-color": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-color/download/d3-color-1.4.0.tgz",
|
||||
"integrity": "sha1-icRamV7Xc7EzFPBkYN8m1gug7K8="
|
||||
},
|
||||
"d3-contour": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-contour/download/d3-contour-1.3.2.tgz",
|
||||
"integrity": "sha1-ZSqs1QDSJkyzQjzuENtp9vWb6tM=",
|
||||
"requires": {
|
||||
"d3-array": "1.2.4"
|
||||
}
|
||||
},
|
||||
"d3-dispatch": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-dispatch/download/d3-dispatch-1.0.6.tgz",
|
||||
"integrity": "sha1-ANN7zuTdjNl3Kd2JOgrCnKq6XVg="
|
||||
},
|
||||
"d3-drag": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-drag/download/d3-drag-1.2.5.tgz",
|
||||
"integrity": "sha1-JTf0UazTnTFAZne33HfIL32Yj3A=",
|
||||
"requires": {
|
||||
"d3-dispatch": "1.0.6",
|
||||
"d3-selection": "1.4.1"
|
||||
}
|
||||
},
|
||||
"d3-dsv": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-dsv/download/d3-dsv-1.2.0.tgz?cache=0&sync_timestamp=1573934576301&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-dsv%2Fdownload%2Fd3-dsv-1.2.0.tgz",
|
||||
"integrity": "sha1-nV91w6X4q9YR900/WEew1DOLiFw=",
|
||||
"requires": {
|
||||
"commander": "2.20.3",
|
||||
"iconv-lite": "0.4.24",
|
||||
"rw": "1.3.3"
|
||||
}
|
||||
},
|
||||
"d3-ease": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-ease/download/d3-ease-1.0.6.tgz",
|
||||
"integrity": "sha1-69ttoi36wKIiIvLU2gb2bEFqDsA="
|
||||
},
|
||||
"d3-fetch": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-fetch/download/d3-fetch-1.1.2.tgz",
|
||||
"integrity": "sha1-lXyPvG1EgFmboZGxslGL+Gs+G+I=",
|
||||
"requires": {
|
||||
"d3-dsv": "1.2.0"
|
||||
}
|
||||
},
|
||||
"d3-force": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-force/download/d3-force-2.0.1.tgz",
|
||||
"integrity": "sha1-MXUO7oxDU1MB1XEZW/loO+2lNOI=",
|
||||
"requires": {
|
||||
"d3-dispatch": "1.0.6",
|
||||
"d3-quadtree": "1.0.7",
|
||||
"d3-timer": "1.0.10"
|
||||
}
|
||||
},
|
||||
"d3-format": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-format/download/d3-format-1.4.3.tgz",
|
||||
"integrity": "sha1-To603/P9y4kahInsbmmGAcQblvE="
|
||||
},
|
||||
"d3-geo": {
|
||||
"version": "1.11.9",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-geo/download/d3-geo-1.11.9.tgz",
|
||||
"integrity": "sha1-d+rtFLpi/CwK71XNKUOEnIZveuY=",
|
||||
"requires": {
|
||||
"d3-array": "1.2.4"
|
||||
}
|
||||
},
|
||||
"d3-hierarchy": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-hierarchy/download/d3-hierarchy-1.1.9.tgz?cache=0&sync_timestamp=1574108571076&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-hierarchy%2Fdownload%2Fd3-hierarchy-1.1.9.tgz",
|
||||
"integrity": "sha1-L2vuJMqupD+Nw3VF+gFihVlkeoM="
|
||||
},
|
||||
"d3-interpolate": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-interpolate/download/d3-interpolate-1.1.6.tgz",
|
||||
@@ -5148,16 +5358,132 @@
|
||||
"d3-color": "1.4.0"
|
||||
}
|
||||
},
|
||||
"d3-path": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-path/download/d3-path-1.0.9.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-path%2Fdownload%2Fd3-path-1.0.9.tgz",
|
||||
"integrity": "sha1-SMBQux/owmJJOoyvVSTj6VkXAc8="
|
||||
},
|
||||
"d3-polygon": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-polygon/download/d3-polygon-1.0.6.tgz",
|
||||
"integrity": "sha1-C/jLgYCm3BB/UY3feXXhKrv7044="
|
||||
},
|
||||
"d3-quadtree": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-quadtree/download/d3-quadtree-1.0.7.tgz",
|
||||
"integrity": "sha1-youE33u1N2P+PC8kvUNRN/TlMTU="
|
||||
},
|
||||
"d3-random": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-random/download/d3-random-1.1.2.tgz",
|
||||
"integrity": "sha1-KDO+fBJDYL+eLT/U8zhHz+bKspE="
|
||||
},
|
||||
"d3-regression": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-regression/download/d3-regression-1.3.4.tgz",
|
||||
"integrity": "sha1-o0FLGr5aINRqII2V5WWkGL6aDiM="
|
||||
},
|
||||
"d3-sankey": {
|
||||
"version": "0.12.3",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-sankey/download/d3-sankey-0.12.3.tgz",
|
||||
"integrity": "sha1-s8JoYnvXLl2AM26N5qy/7J0V0B0=",
|
||||
"requires": {
|
||||
"d3-array": "1.2.4",
|
||||
"d3-shape": "1.3.7"
|
||||
}
|
||||
},
|
||||
"d3-scale": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-scale/download/d3-scale-2.2.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-scale%2Fdownload%2Fd3-scale-2.2.2.tgz",
|
||||
"integrity": "sha1-TogOCydFrKrd0+3iap6Qip4XuB8=",
|
||||
"requires": {
|
||||
"d3-array": "1.2.4",
|
||||
"d3-collection": "1.0.7",
|
||||
"d3-format": "1.4.3",
|
||||
"d3-interpolate": "1.1.6",
|
||||
"d3-time": "1.1.0",
|
||||
"d3-time-format": "2.2.3"
|
||||
}
|
||||
},
|
||||
"d3-scale-chromatic": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-scale-chromatic/download/d3-scale-chromatic-1.5.0.tgz",
|
||||
"integrity": "sha1-VOMz/HghL0ObFGQftVgB3YETWpg=",
|
||||
"requires": {
|
||||
"d3-color": "1.4.0",
|
||||
"d3-interpolate": "1.1.6"
|
||||
}
|
||||
},
|
||||
"d3-selection": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-selection/download/d3-selection-1.4.1.tgz",
|
||||
"integrity": "sha1-mO7bvghfvaW6+i+ePzovTX1iKpg="
|
||||
},
|
||||
"d3-shape": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-shape/download/d3-shape-1.3.7.tgz",
|
||||
"integrity": "sha1-32OAG+B7yYa8VPY3ibT+UCmStdc=",
|
||||
"requires": {
|
||||
"d3-path": "1.0.9"
|
||||
}
|
||||
},
|
||||
"d3-time": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-time/download/d3-time-1.1.0.tgz",
|
||||
"integrity": "sha1-seGdMH2unJALflsl/8XcwkmooPE="
|
||||
},
|
||||
"d3-time-format": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-time-format/download/d3-time-format-2.2.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-time-format%2Fdownload%2Fd3-time-format-2.2.3.tgz",
|
||||
"integrity": "sha1-DJoS7ig0KyA35eoc8LnrTddfKcs=",
|
||||
"requires": {
|
||||
"d3-time": "1.1.0"
|
||||
}
|
||||
},
|
||||
"d3-timer": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-timer/download/d3-timer-1.0.10.tgz?cache=0&sync_timestamp=1573934576216&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fd3-timer%2Fdownload%2Fd3-timer-1.0.10.tgz",
|
||||
"integrity": "sha1-3+dripF0iDGxO22ceT/71QjdneU="
|
||||
},
|
||||
"d3-transition": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-transition/download/d3-transition-1.3.2.tgz",
|
||||
"integrity": "sha1-qY7yFRvo2GAFQ0NMHKgBQK4js5g=",
|
||||
"requires": {
|
||||
"d3-color": "1.4.0",
|
||||
"d3-dispatch": "1.0.6",
|
||||
"d3-ease": "1.0.6",
|
||||
"d3-interpolate": "1.1.6",
|
||||
"d3-selection": "1.4.1",
|
||||
"d3-timer": "1.0.10"
|
||||
}
|
||||
},
|
||||
"d3-voronoi": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-voronoi/download/d3-voronoi-1.1.4.tgz",
|
||||
"integrity": "sha1-3Tx412U9K7NZKErkeGRdlZRMgpc="
|
||||
},
|
||||
"d3-zoom": {
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npm.taobao.org/d3-zoom/download/d3-zoom-1.8.3.tgz",
|
||||
"integrity": "sha1-tqPb5zjHdjEhzQW4p3lf/hf0/Ao=",
|
||||
"requires": {
|
||||
"d3-dispatch": "1.0.6",
|
||||
"d3-drag": "1.2.5",
|
||||
"d3-interpolate": "1.1.6",
|
||||
"d3-selection": "1.4.1",
|
||||
"d3-transition": "1.3.2"
|
||||
}
|
||||
},
|
||||
"dagre": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npm.taobao.org/dagre/download/dagre-0.8.5.tgz",
|
||||
"integrity": "sha1-ujCwBV2sErbB/MJHgXRCd30Gr+4=",
|
||||
"requires": {
|
||||
"graphlib": "2.1.8",
|
||||
"lodash": "4.17.15"
|
||||
}
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz",
|
||||
@@ -7568,6 +7894,14 @@
|
||||
"integrity": "sha1-ShL/G2A3bvCYYsIJPt2Qgyi+hCM=",
|
||||
"dev": true
|
||||
},
|
||||
"graphlib": {
|
||||
"version": "2.1.8",
|
||||
"resolved": "https://registry.npm.taobao.org/graphlib/download/graphlib-2.1.8.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fgraphlib%2Fdownload%2Fgraphlib-2.1.8.tgz",
|
||||
"integrity": "sha1-V2HUFHN4cAhMkux7XbywWSydNdo=",
|
||||
"requires": {
|
||||
"lodash": "4.17.15"
|
||||
}
|
||||
},
|
||||
"gray-matter": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npm.taobao.org/gray-matter/download/gray-matter-4.0.2.tgz",
|
||||
@@ -8023,7 +8357,6 @@
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz?cache=0&sync_timestamp=1579333981154&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ficonv-lite%2Fdownload%2Ficonv-lite-0.4.24.tgz",
|
||||
"integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": "2.1.2"
|
||||
}
|
||||
@@ -9306,8 +9639,7 @@
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.15.tgz?cache=0&sync_timestamp=1563508077056&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.15.tgz",
|
||||
"integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg=",
|
||||
"dev": true
|
||||
"integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg="
|
||||
},
|
||||
"lodash._reinterpolate": {
|
||||
"version": "3.0.0",
|
||||
@@ -10296,6 +10628,16 @@
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true
|
||||
},
|
||||
"numeric": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npm.taobao.org/numeric/download/numeric-1.2.6.tgz",
|
||||
"integrity": "sha1-dlsCvvl5iPz4gNTrPza4D6MTNao="
|
||||
},
|
||||
"numericjs": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npm.taobao.org/numericjs/download/numericjs-1.2.6.tgz",
|
||||
"integrity": "sha1-wNryXEvLIuBDv4NEP5856LM2eYs="
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.9.0.tgz",
|
||||
@@ -13155,6 +13497,11 @@
|
||||
"aproba": "1.2.0"
|
||||
}
|
||||
},
|
||||
"rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npm.taobao.org/rw/download/rw-1.3.3.tgz",
|
||||
"integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npm.taobao.org/rxjs/download/rxjs-6.5.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frxjs%2Fdownload%2Frxjs-6.5.4.tgz",
|
||||
@@ -13182,8 +13529,7 @@
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=",
|
||||
"dev": true
|
||||
"integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo="
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
|
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/g2plot": "^0.11.22",
|
||||
"@antv/g6": "^3.2.9",
|
||||
"core-js": "^3.4.4",
|
||||
"view-design": "^4.0.0",
|
||||
"vue": "^2.6.10",
|
||||
@@ -48,6 +49,7 @@
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"vue/no-parsing-error": [
|
||||
2,
|
||||
{
|
||||
|
@@ -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
@@ -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 |
@@ -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}}]);
|
@@ -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}}]);
|
@@ -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}}]);
|
@@ -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}}]);
|
@@ -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}}]);
|
@@ -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}}]);
|
@@ -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
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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>
|
@@ -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, {});
|
@@ -1,80 +1,71 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<div>Monibuca</div>
|
||||
<Menu mode="horizontal" :active-name="selectedMenu" style="position: absolute;top: 0;right: 0;">
|
||||
<MenuItem name="home" to="/">首页</MenuItem>
|
||||
<MenuItem name="docs" to="/docs" target="_blank">
|
||||
文档
|
||||
</MenuItem>
|
||||
<MenuItem name="console" to="console">
|
||||
控制台
|
||||
</MenuItem>
|
||||
<Submenu name="plugins">
|
||||
<template slot="title">
|
||||
内置插件
|
||||
</template>
|
||||
<MenuGroup title="发布者/订阅者">
|
||||
<MenuItem name="cluster">集群</MenuItem>
|
||||
<MenuItem name="rtmp">RTMP</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup title="订阅者">
|
||||
<MenuItem name="jessica">Jessica</MenuItem>
|
||||
<MenuItem name="HDL">Http-Flv</MenuItem>
|
||||
<MenuItem name="record">录制Flv</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup title="发布者">
|
||||
<MenuItem name="HLS">HLS</MenuItem>
|
||||
<MenuItem name="TS">TS</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup title="钩子">
|
||||
<MenuItem name="Auth">验证</MenuItem>
|
||||
<MenuItem name="QoS">QoS</MenuItem>
|
||||
<MenuItem name="gateway">网关</MenuItem>
|
||||
</MenuGroup>
|
||||
</Submenu>
|
||||
<MenuItem name="4" to="about">
|
||||
支持
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<router-view class="content"></router-view>
|
||||
<div>Copyright © 2019-2020 dexter 苏ICP备20001212号</div>
|
||||
</div>
|
||||
<div id="app">
|
||||
<div>Monibuca</div>
|
||||
<Menu mode="horizontal" :active-name="selectedMenu" style="position: absolute;top: 0;right: 0;">
|
||||
<MenuItem name="home" to="/">首页</MenuItem>
|
||||
<MenuItem name="docs" to="/docs" target="_blank">文档</MenuItem>
|
||||
<MenuItem name="console" to="console">控制台</MenuItem>
|
||||
<Submenu name="plugins">
|
||||
<template slot="title">内置插件</template>
|
||||
<MenuGroup title="发布者/订阅者">
|
||||
<MenuItem name="cluster" target="_blank" to="/docs/plugins.html#cluster插件">集群</MenuItem>
|
||||
<MenuItem name="rtmp" target="_blank" to="/docs/plugins.html#rtmp插件">RTMP</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup title="订阅者">
|
||||
<MenuItem name="jessica" target="_blank" to="/docs/plugins.html#jessica插件">Jessica</MenuItem>
|
||||
<MenuItem name="HDL" target="_blank" to="/docs/plugins.html#http-flv插件">Http-Flv</MenuItem>
|
||||
<MenuItem name="record" target="_blank" to="/docs/plugins.html#recordflv插件">录制Flv</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup title="发布者">
|
||||
<MenuItem name="HLS" target="_blank" to="/docs/plugins.html#hls插件">HLS</MenuItem>
|
||||
<MenuItem name="TS" target="_blank" to="/docs/plugins.html#hls插件">TS</MenuItem>
|
||||
</MenuGroup>
|
||||
<MenuGroup title="钩子">
|
||||
<MenuItem name="Auth" target="_blank" to="/docs/plugins.html#校验插件">验证</MenuItem>
|
||||
<MenuItem name="QoS">QoS</MenuItem>
|
||||
<MenuItem name="gateway" target="_blank" to="/docs/plugins.html#网关插件">网关</MenuItem>
|
||||
</MenuGroup>
|
||||
</Submenu>
|
||||
<MenuItem name="4" to="about">支持</MenuItem>
|
||||
</Menu>
|
||||
<router-view class="content"></router-view>
|
||||
<div>Copyright © 2019-2020 dexter 苏ICP备20001212号</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
data(){
|
||||
return {
|
||||
selectedMenu:"home"
|
||||
}
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: "app",
|
||||
data() {
|
||||
return {
|
||||
selectedMenu: "home"
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
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;
|
||||
height: 100%;
|
||||
}
|
||||
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;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
#app > div:first-child {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 30px;
|
||||
font-size: x-large;
|
||||
}
|
||||
.content{
|
||||
padding-top: 60px;
|
||||
}
|
||||
#app > div:first-child {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 30px;
|
||||
font-size: x-large;
|
||||
}
|
||||
.content {
|
||||
padding-top: 60px;
|
||||
}
|
||||
</style>
|
||||
|
97
dashboard/src/components/Cluster.vue
Normal file
97
dashboard/src/components/Cluster.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div id="mountNode"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import G6 from "@antv/g6";
|
||||
var graph = null;
|
||||
export default {
|
||||
computed: {
|
||||
...mapState({
|
||||
data(state) {
|
||||
let summary = state.summary;
|
||||
// 点集
|
||||
let nodes = [];
|
||||
// 边集
|
||||
let edges = [];
|
||||
this.addServer(summary, nodes, edges);
|
||||
return {
|
||||
nodes,
|
||||
edges
|
||||
};
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
addServer(node, nodes, edges) {
|
||||
let result = {
|
||||
id: node.Address,
|
||||
label: node.Address,
|
||||
description: `cpu:${node.CPUUsage >> 0}% mem:${node.Memory.Usage >>
|
||||
0}%`,
|
||||
shape: "modelRect",
|
||||
logoIcon: {
|
||||
show: false
|
||||
}
|
||||
};
|
||||
nodes.push(result);
|
||||
if (node.Rooms) {
|
||||
for (let i = 0; i < node.Rooms.length; i++) {
|
||||
let room = node.Rooms[i];
|
||||
let roomId = result.id + room.StreamPath;
|
||||
nodes.push({
|
||||
id: roomId,
|
||||
label: room.StreamPath,
|
||||
shape: "rect"
|
||||
});
|
||||
edges.push({ source: result.id, target: roomId });
|
||||
if (room.SubscriberInfo) {
|
||||
for (let j = 0; j < room.SubscriberInfo.length; j++) {
|
||||
let subId = roomId + room.SubscriberInfo[j].ID;
|
||||
nodes.push({
|
||||
id: subId,
|
||||
label: room.SubscriberInfo[j].ID
|
||||
});
|
||||
edges.push({ source: roomId, target: subId });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node.Children && node.Children.length > 0) {
|
||||
for (let i = 0; i < node.Children.length; i++) {
|
||||
let child = this.addServer(node.Children[i], nodes, edges);
|
||||
edges.push({
|
||||
source: result.id,
|
||||
target: child.id
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data(v) {
|
||||
if (graph) {
|
||||
graph.read(v); // 加载数据
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
graph = new G6.Graph({
|
||||
renderer: "svg",
|
||||
container: "mountNode", // 指定挂载容器
|
||||
width: 800, // 图的宽度
|
||||
height: 500, // 图的高度
|
||||
layout: {
|
||||
type: "radial"
|
||||
},
|
||||
defaultNode: {}
|
||||
});
|
||||
graph.read(this.data); // 加载数据
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
29
dashboard/src/components/Config.vue
Normal file
29
dashboard/src/components/Config.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div style="padding:0 15px">
|
||||
<pre>{{config}}</pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
config: ""
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onVisible(visible) {
|
||||
if (visible) {
|
||||
window.ajax.get(
|
||||
"//" + location.host + "/api/config",
|
||||
{},
|
||||
x => (this.config = x)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
@@ -1,50 +1,60 @@
|
||||
<template>
|
||||
<Modal
|
||||
v-bind="$attrs" draggable
|
||||
v-on="$listeners"
|
||||
:title="url"
|
||||
@on-ok="onClosePreview"
|
||||
@on-cancel="onClosePreview">
|
||||
<canvas id="canvas" width="488" height="275" style="background: black"/>
|
||||
</Modal>
|
||||
<Modal
|
||||
v-bind="$attrs"
|
||||
draggable
|
||||
v-on="$listeners"
|
||||
:title="url"
|
||||
@on-ok="onClosePreview"
|
||||
@on-cancel="onClosePreview"
|
||||
>
|
||||
<canvas id="canvas" width="488" height="275" style="background: black" />
|
||||
<div slot="footer">
|
||||
<Button v-if="audioEnabled" @click="turnOff" icon="md-volume-off" />
|
||||
<Button v-else @click="turnOn" icon="md-volume-up"></Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let h5lc = null;
|
||||
export default {
|
||||
name: 'Jessibuca',
|
||||
props: {
|
||||
audioEnabled: Boolean,
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
url:""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
audioEnabled(value){
|
||||
h5lc.audioEnabled(value)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
h5lc = new window.Jessibuca({
|
||||
canvas: document.getElementById("canvas"),
|
||||
decoder: "jessibuca/ff.js"
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
this.onClosePreview()
|
||||
h5lc.destroy()
|
||||
},
|
||||
methods: {
|
||||
play(url){
|
||||
this.url = url
|
||||
h5lc.play(url)
|
||||
},
|
||||
onClosePreview() {
|
||||
h5lc.close();
|
||||
},
|
||||
}
|
||||
let h5lc = null;
|
||||
export default {
|
||||
name: "Jessibuca",
|
||||
data() {
|
||||
return {
|
||||
audioEnabled: false,
|
||||
url: ""
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
audioEnabled(value) {
|
||||
h5lc.audioEnabled(value);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
h5lc = new window.Jessibuca({
|
||||
canvas: document.getElementById("canvas"),
|
||||
decoder: "jessibuca/ff.js"
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
this.onClosePreview();
|
||||
h5lc.destroy();
|
||||
},
|
||||
methods: {
|
||||
play(url) {
|
||||
this.url = url;
|
||||
h5lc.play(url);
|
||||
},
|
||||
onClosePreview() {
|
||||
h5lc.close();
|
||||
},
|
||||
turnOn() {
|
||||
this.audioEnabled = true;
|
||||
},
|
||||
turnOff() {
|
||||
this.audioEnabled = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
43
dashboard/src/components/Logs.vue
Normal file
43
dashboard/src/components/Logs.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div style="padding:0 15px">
|
||||
<div>
|
||||
自动滚动
|
||||
<Switch v-model="autoScroll" />
|
||||
</div>
|
||||
<div ref="logContainer" class="log-container">
|
||||
<pre><template v-for="item in $store.state.logs">{{item+"\n"}}</template></pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions } from "vuex";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
autoScroll: true
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.fetchLogs();
|
||||
},
|
||||
destroyed() {
|
||||
this.stopFetchLogs();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["fetchLogs", "stopFetchLogs"])
|
||||
},
|
||||
updated() {
|
||||
if (this.autoScroll) {
|
||||
this.$refs.logContainer.scrollTop = this.$refs.logContainer.offsetHeight;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.log-container {
|
||||
overflow-y: auto;
|
||||
max-height: 500px;
|
||||
}
|
||||
</style>
|
112
dashboard/src/components/Records.vue
Normal file
112
dashboard/src/components/Records.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="records">
|
||||
<Card v-for="item in data" :key="item">
|
||||
<p slot="title">{{item.Path}}</p>
|
||||
<div slot="extra">
|
||||
<Button @click="play(item)" icon="md-play" size="small"></Button>
|
||||
<Button @click="deleteFlv(item)" icon="ios-trash" size="small"></Button>
|
||||
</div>
|
||||
{{toSizeStr(item.Size)}} {{toDurationStr(item.Duration)}}
|
||||
</Card>
|
||||
</div>
|
||||
</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.onVisible(true);
|
||||
this.$Message.success("删除成功");
|
||||
} else {
|
||||
this.$Message.error(x);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
deleteFlv(item) {
|
||||
this.$Modal.confirm({
|
||||
title: "提示",
|
||||
content: "<p>是否删除Flv文件</p>",
|
||||
onOk: () => {
|
||||
window.ajax.get(
|
||||
"//" + location.host + "/api/record/flv/delete",
|
||||
{ streamPath: item.Path.replace(".flv", "") },
|
||||
x => {
|
||||
if (x == "success") {
|
||||
this.$Message.success("开始发布");
|
||||
} else {
|
||||
this.$Message.error(x);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
onCancel: () => {}
|
||||
});
|
||||
},
|
||||
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";
|
||||
}
|
||||
},
|
||||
onVisible(visible) {
|
||||
if (visible) {
|
||||
window.ajax.getJSON(
|
||||
"//" + location.host + "/api/record/flv/list",
|
||||
{},
|
||||
x => {
|
||||
this.data = x;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.records {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 15px;
|
||||
}
|
||||
.records > * {
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
62
dashboard/src/components/Subscribers.vue
Normal file
62
dashboard/src/components/Subscribers.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<Modal v-bind="$attrs" draggable v-on="$listeners" title="查看订阅者">
|
||||
<Table :columns="subtableColumns" :data="data"></Table>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
data: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
subtableColumns: [
|
||||
{
|
||||
title: "类型",
|
||||
key: "Type"
|
||||
},
|
||||
{
|
||||
title: "Name",
|
||||
key: "ID"
|
||||
},
|
||||
{
|
||||
title: "订阅时间",
|
||||
render(h, { row }) {
|
||||
return h("StartTime", {
|
||||
props: {
|
||||
value: row.SubscribeTime
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "丢帧",
|
||||
render(h, { row }) {
|
||||
return h(
|
||||
"span",
|
||||
row.TotalPacket ? row.TotalDrop + "/" + row.TotalPacket : ""
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Buffer",
|
||||
render(h, { row }) {
|
||||
return h("Progress", {
|
||||
props: {
|
||||
percent: Math.floor((row.BufferLength * 99) / 1024),
|
||||
"text-inside": true,
|
||||
"stroke-width": 20,
|
||||
"stroke-color": ["#87d068", "#ff0000"]
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
@@ -3,40 +3,58 @@ import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex)
|
||||
let summaryES = null
|
||||
|
||||
let logsES = null
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
summary:{
|
||||
NetWork:[],
|
||||
Rooms:[],
|
||||
Memory:{
|
||||
summary: {
|
||||
Address: location.hostname,
|
||||
NetWork: [],
|
||||
Rooms: [],
|
||||
Memory: {
|
||||
Used: 0,
|
||||
Usage: 0
|
||||
},
|
||||
CPUUsage:0,
|
||||
HardDisk:{
|
||||
CPUUsage: 0,
|
||||
HardDisk: {
|
||||
Used: 0,
|
||||
Usage: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
Children: {}
|
||||
}, logs: []
|
||||
},
|
||||
mutations: {
|
||||
update(state,payload){
|
||||
Object.assign(state,payload)
|
||||
update(state, payload) {
|
||||
Object.assign(state, payload)
|
||||
},
|
||||
addLog(state, payload) {
|
||||
state.logs.push(payload)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
fetchSummary({commit}){
|
||||
fetchSummary({ commit }) {
|
||||
summaryES = new EventSource(
|
||||
"//" + location.host + "/api/summary"
|
||||
"//" + location.host + "/api/summary"
|
||||
);
|
||||
summaryES.onmessage = evt=>{
|
||||
summaryES.onmessage = evt => {
|
||||
if (!evt.data) return
|
||||
let summary = JSON.parse(evt.data)
|
||||
commit("update",{summary})
|
||||
summary.Address = location.hostname
|
||||
commit("update", { summary })
|
||||
}
|
||||
},
|
||||
stopFetchSummary(){
|
||||
fetchLogs({ commit }) {
|
||||
logsES = new EventSource(
|
||||
"//" + location.host + "/api/logs"
|
||||
)
|
||||
logsES.onmessage = evt => {
|
||||
if (!evt.data) return
|
||||
commit("addLog", evt.data)
|
||||
}
|
||||
},
|
||||
stopFetchLogs() {
|
||||
logsES.close()
|
||||
},
|
||||
stopFetchSummary() {
|
||||
summaryES.close()
|
||||
}
|
||||
},
|
||||
|
@@ -1,232 +1,325 @@
|
||||
<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>
|
||||
<div style="text-align:left;">
|
||||
<Tabs v-model="currentTab" @on-click="onChangeTab">
|
||||
<TabPane label="直播流" icon="md-videocam">
|
||||
<div class="layout">
|
||||
<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}}
|
||||
{{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}}
|
||||
{{CodecID(item.VideoInfo.CodecID)}} {{item.VideoInfo.PacketCount}}
|
||||
{{item.VideoInfo.SPSInfo.Width}}x{{item.VideoInfo.SPSInfo.Height}}
|
||||
</p>
|
||||
<Button @click="onShowDetail(item)">
|
||||
<Icon type="ios-people"/>
|
||||
{{item.SubscriberInfo?item.SubscriberInfo.length:0}}
|
||||
</Button>
|
||||
<Button v-if="item.Type" @click="preview(item)">
|
||||
<Icon type="md-eye"/>
|
||||
Preview
|
||||
</Button>
|
||||
<Button @click="stopRecord(item)" v-if="isRecording(item)">
|
||||
<Icon type="ios-radio-button-on" class="recording"/>
|
||||
Stop Rec
|
||||
</Button>
|
||||
<Button @click="record(item)" v-else>
|
||||
<Icon type="ios-radio-button-on"/>
|
||||
Rec
|
||||
</Button>
|
||||
</Card>
|
||||
<div v-if="Rooms.length==0" class="empty">
|
||||
<Icon type="md-wine" size="50"/>
|
||||
没有任何房间
|
||||
<ButtonGroup size="small">
|
||||
<Button @click="onShowDetail(item)" icon="ios-people">{{getSubscriberCount(item)}}</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>
|
||||
<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>
|
||||
</TabPane>
|
||||
<TabPane label="集群总览" icon="ios-cloud">
|
||||
<Cluster />
|
||||
</TabPane>
|
||||
<TabPane label="录制的视频" icon="ios-folder" name="recordsPanel">
|
||||
<Records ref="recordsPanel" />
|
||||
</TabPane>
|
||||
<TabPane label="日志跟踪" icon="md-bug">
|
||||
<Logs />
|
||||
</TabPane>
|
||||
<TabPane label="查看配置" icon="md-settings" name="configPanel">
|
||||
<Config ref="configPanel" />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<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>
|
||||
<Subscribers :data="currentStream && currentStream.SubscriberInfo" v-model="showSubscribers" />
|
||||
</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: "📸"
|
||||
}
|
||||
import { mapActions, mapState } from "vuex";
|
||||
import Jessibuca from "../components/Jessibuca";
|
||||
import StartTime from "../components/StartTime";
|
||||
import Records from "../components/Records";
|
||||
import Logs from "../components/Logs";
|
||||
import Config from "../components/Config";
|
||||
import Subscribers from "../components/Subscribers";
|
||||
import Cluster from "../components/Cluster";
|
||||
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,
|
||||
Logs,
|
||||
Subscribers,
|
||||
Config,
|
||||
Cluster
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showPreview: false,
|
||||
showSubscribers: false,
|
||||
currentTab: "",
|
||||
currentStream: [],
|
||||
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
|
||||
? state.summary.NetWork.reduce(
|
||||
(aac, c) => aac + c.ReceiveSpeed,
|
||||
0
|
||||
)
|
||||
: 0
|
||||
) + "/S"
|
||||
);
|
||||
},
|
||||
totalOutNetSpeed(state) {
|
||||
return (
|
||||
this.networkFormat(
|
||||
state.summary.NetWork
|
||||
? state.summary.NetWork.reduce((aac, c) => aac + c.SentSpeed, 0)
|
||||
: 0
|
||||
) + "/S"
|
||||
);
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["fetchSummary", "stopFetchSummary"]),
|
||||
getSubscriberCount(item) {
|
||||
if (
|
||||
this.currentStream &&
|
||||
this.currentStream.StreamPath == item.StreamPath
|
||||
) {
|
||||
this.currentStream = item;
|
||||
}
|
||||
return item.SubscriberInfo ? item.SubscriberInfo.length : 0;
|
||||
},
|
||||
preview(item) {
|
||||
this.$refs.jessibuca.play(
|
||||
"ws://" + location.hostname + ":8080/" + item.StreamPath
|
||||
);
|
||||
this.showPreview = true;
|
||||
},
|
||||
onShowDetail(item) {
|
||||
this.showSubscribers = true;
|
||||
this.currentStream = 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) {
|
||||
this.$Modal.confirm({
|
||||
title: "提示",
|
||||
content: "<p>是否使用追加模式</p><small>选择取消将覆盖已有文件</small>",
|
||||
onOk: () => {
|
||||
window.ajax.get(
|
||||
"//" + location.host + "/api/record/flv?append=true",
|
||||
{ streamPath: item.StreamPath },
|
||||
x => {
|
||||
if (x == "success") {
|
||||
this.$Message.success("开始录制(追加模式)");
|
||||
} else {
|
||||
this.$Message.error(x);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
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})
|
||||
},
|
||||
stopRecord(item){
|
||||
window.ajax.get("//" + location.host + "/api/record/flv/stop",{streamPath:item.StreamPath})
|
||||
},
|
||||
isRecording(item) {
|
||||
return item.SubscriberInfo && item.SubscriberInfo.find(x => x.Type == "FlvRecord")
|
||||
onCancel: () => {
|
||||
window.ajax.get(
|
||||
"//" + location.host + "/api/record/flv",
|
||||
{ streamPath: item.StreamPath },
|
||||
x => {
|
||||
if (x == "success") {
|
||||
this.$Message.success("开始录制");
|
||||
} else {
|
||||
this.$Message.error(x);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchSummary()
|
||||
},
|
||||
destroyed() {
|
||||
this.stopFetchSummary()
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
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")
|
||||
);
|
||||
},
|
||||
onChangeTab(name) {
|
||||
switch (name) {
|
||||
case "recordsPanel":
|
||||
this.$refs.recordsPanel.onVisible(true);
|
||||
break;
|
||||
case "configPanel":
|
||||
this.$refs.configPanel.onVisible(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
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;
|
||||
position: relative;
|
||||
}
|
||||
.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>
|
5
main.go
5
main.go
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
. "github.com/langhuihui/monibuca/monica"
|
||||
_ "github.com/langhuihui/monibuca/plugins"
|
||||
"log"
|
||||
@@ -9,6 +10,8 @@ import (
|
||||
|
||||
func main() {
|
||||
log.SetOutput(os.Stdout)
|
||||
Run("config.toml")
|
||||
configPath := flag.String("c", "config.toml", "configFile")
|
||||
flag.Parse()
|
||||
Run(*configPath)
|
||||
select {}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -54,3 +54,16 @@ func (h OnDropHook) Trigger(s *OutputStream) {
|
||||
h(s)
|
||||
}
|
||||
}
|
||||
|
||||
var OnSummaryHooks = make(OnSummaryHook, 0)
|
||||
|
||||
type OnSummaryHook []func(bool)
|
||||
|
||||
func (h OnSummaryHook) AddHook(hook func(bool)) {
|
||||
OnSummaryHooks = append(h, hook)
|
||||
}
|
||||
func (h OnSummaryHook) Trigger(v bool) {
|
||||
for _, h := range h {
|
||||
h(v)
|
||||
}
|
||||
}
|
||||
|
@@ -3,11 +3,18 @@ package monica
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/BurntSushi/toml"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
var ConfigRaw []byte
|
||||
|
||||
func Run(configFile string) (err error) {
|
||||
if _, err = toml.DecodeFile(configFile, cg); err == nil {
|
||||
if ConfigRaw, err = ioutil.ReadFile(configFile); err != nil {
|
||||
return
|
||||
}
|
||||
go Summary.StartSummary()
|
||||
if _, err = toml.Decode(string(ConfigRaw), cg); err == nil {
|
||||
for name, config := range plugins {
|
||||
if cfg, ok := cg.Plugins[name]; ok {
|
||||
b, _ := json.Marshal(cfg)
|
||||
|
@@ -111,11 +111,13 @@ func (r *Room) Run() {
|
||||
case <-r.Done():
|
||||
return
|
||||
case <-update.C:
|
||||
r.SubscriberInfo = make([]*SubscriberInfo, len(r.Subscribers))
|
||||
i := 0
|
||||
for _, v := range r.Subscribers {
|
||||
r.SubscriberInfo[i] = &v.SubscriberInfo
|
||||
i++
|
||||
if Summary.Running() {
|
||||
r.SubscriberInfo = make([]*SubscriberInfo, len(r.Subscribers))
|
||||
i := 0
|
||||
for _, v := range r.Subscribers {
|
||||
r.SubscriberInfo[i] = &v.SubscriberInfo
|
||||
i++
|
||||
}
|
||||
}
|
||||
case s := <-r.Control:
|
||||
switch v := s.(type) {
|
||||
@@ -154,7 +156,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
|
||||
|
139
monica/summary.go
Normal file
139
monica/summary.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package monica
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/shirou/gopsutil/net"
|
||||
)
|
||||
|
||||
var (
|
||||
Summary = ServerSummary{}
|
||||
)
|
||||
|
||||
type ServerSummary struct {
|
||||
Address string
|
||||
Memory struct {
|
||||
Total uint64
|
||||
Free uint64
|
||||
Used uint64
|
||||
Usage float64
|
||||
}
|
||||
CPUUsage float64
|
||||
HardDisk struct {
|
||||
Total uint64
|
||||
Free uint64
|
||||
Used uint64
|
||||
Usage float64
|
||||
}
|
||||
NetWork []NetWorkInfo
|
||||
Rooms []*RoomInfo
|
||||
lastNetWork []NetWorkInfo
|
||||
ref int
|
||||
control chan bool
|
||||
reportChan chan *ServerSummary
|
||||
Children map[string]*ServerSummary
|
||||
}
|
||||
type NetWorkInfo struct {
|
||||
Name string
|
||||
Receive uint64
|
||||
Sent uint64
|
||||
ReceiveSpeed uint64
|
||||
SentSpeed uint64
|
||||
}
|
||||
|
||||
func (s *ServerSummary) StartSummary() {
|
||||
ticker := time.NewTicker(time.Second)
|
||||
s.control = make(chan bool)
|
||||
s.reportChan = make(chan *ServerSummary)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if s.ref > 0 {
|
||||
Summary.collect()
|
||||
}
|
||||
case v := <-s.control:
|
||||
if v {
|
||||
if s.ref++; s.ref == 1 {
|
||||
OnSummaryHooks.Trigger(true)
|
||||
}
|
||||
} else {
|
||||
if s.ref--; s.ref == 0 {
|
||||
s.lastNetWork = nil
|
||||
OnSummaryHooks.Trigger(false)
|
||||
}
|
||||
}
|
||||
case report := <-s.reportChan:
|
||||
s.Children[report.Address] = report
|
||||
}
|
||||
}
|
||||
}
|
||||
func (s *ServerSummary) Running() bool {
|
||||
return s.ref > 0
|
||||
}
|
||||
func (s *ServerSummary) Add() {
|
||||
s.control <- true
|
||||
}
|
||||
func (s *ServerSummary) Done() {
|
||||
s.control <- false
|
||||
}
|
||||
func (s *ServerSummary) Report(slave *ServerSummary) {
|
||||
s.reportChan <- slave
|
||||
}
|
||||
func (s *ServerSummary) collect() {
|
||||
v, _ := mem.VirtualMemory()
|
||||
//c, _ := cpu.Info()
|
||||
cc, _ := cpu.Percent(time.Second, false)
|
||||
d, _ := disk.Usage("/")
|
||||
//n, _ := host.Info()
|
||||
nv, _ := net.IOCounters(true)
|
||||
//boottime, _ := host.BootTime()
|
||||
//btime := time.Unix(int64(boottime), 0).Format("2006-01-02 15:04:05")
|
||||
s.Memory.Total = v.Total / 1024 / 1024
|
||||
s.Memory.Free = v.Available / 1024 / 1024
|
||||
s.Memory.Used = v.Used / 1024 / 1024
|
||||
s.Memory.Usage = v.UsedPercent
|
||||
//fmt.Printf(" Mem : %v MB Free: %v MB Used:%v Usage:%f%%\n", v.Total/1024/1024, v.Available/1024/1024, v.Used/1024/1024, v.UsedPercent)
|
||||
//if len(c) > 1 {
|
||||
// for _, sub_cpu := range c {
|
||||
// modelname := sub_cpu.ModelName
|
||||
// cores := sub_cpu.Cores
|
||||
// fmt.Printf(" CPU : %v %v cores \n", modelname, cores)
|
||||
// }
|
||||
//} else {
|
||||
// sub_cpu := c[0]
|
||||
// modelname := sub_cpu.ModelName
|
||||
// cores := sub_cpu.Cores
|
||||
// fmt.Printf(" CPU : %v %v cores \n", modelname, cores)
|
||||
//}
|
||||
s.CPUUsage = cc[0]
|
||||
s.HardDisk.Free = d.Free / 1024 / 1024 / 1024
|
||||
s.HardDisk.Total = d.Total / 1024 / 1024 / 1024
|
||||
s.HardDisk.Used = d.Used / 1024 / 1024 / 1024
|
||||
s.HardDisk.Usage = d.UsedPercent
|
||||
s.NetWork = make([]NetWorkInfo, len(nv))
|
||||
for i, n := range nv {
|
||||
s.NetWork[i].Name = n.Name
|
||||
s.NetWork[i].Receive = n.BytesRecv
|
||||
s.NetWork[i].Sent = n.BytesSent
|
||||
if s.lastNetWork != nil && len(s.lastNetWork) > i {
|
||||
s.NetWork[i].ReceiveSpeed = n.BytesRecv - s.lastNetWork[i].Receive
|
||||
s.NetWork[i].SentSpeed = n.BytesSent - s.lastNetWork[i].Sent
|
||||
}
|
||||
}
|
||||
s.lastNetWork = s.NetWork
|
||||
//fmt.Printf(" Network: %v bytes / %v bytes\n", nv[0].BytesRecv, nv[0].BytesSent)
|
||||
//fmt.Printf(" SystemBoot:%v\n", btime)
|
||||
//fmt.Printf(" CPU Used : used %f%% \n", cc[0])
|
||||
//fmt.Printf(" HD : %v GB Free: %v GB Usage:%f%%\n", d.Total/1024/1024/1024, d.Free/1024/1024/1024, d.UsedPercent)
|
||||
//fmt.Printf(" OS : %v(%v) %v \n", n.Platform, n.PlatformFamily, n.PlatformVersion)
|
||||
//fmt.Printf(" Hostname : %v \n", n.Hostname)
|
||||
s.Rooms = nil
|
||||
AllRoom.Range(func(key interface{}, v interface{}) bool {
|
||||
s.Rooms = append(s.Rooms, &v.(*Room).RoomInfo)
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
@@ -4,14 +4,15 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
. "github.com/langhuihui/monibuca/monica"
|
||||
"github.com/quangngotan95/go-m3u8/m3u8"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
. "github.com/langhuihui/monibuca/monica"
|
||||
"github.com/quangngotan95/go-m3u8/m3u8"
|
||||
)
|
||||
|
||||
type HLS struct {
|
||||
@@ -94,8 +95,8 @@ func (p *HLS) run(info *M3u8Info) {
|
||||
if len(tsItems) > 3 {
|
||||
tsItems = tsItems[len(tsItems)-3:]
|
||||
}
|
||||
info.M3u8Info = make([]TSCost, len(tsItems))
|
||||
for i, v := range tsItems {
|
||||
info.M3u8Info = nil
|
||||
for _, v := range tsItems {
|
||||
tsCost := TSCost{}
|
||||
tsUrl, _ := info.Req.URL.Parse(v.Segment)
|
||||
tsReq, _ := http.NewRequest("GET", tsUrl.String(), nil)
|
||||
@@ -122,7 +123,7 @@ func (p *HLS) run(info *M3u8Info) {
|
||||
} else if err != nil {
|
||||
log.Printf("%s reqTs:%v", p.StreamPath, err)
|
||||
}
|
||||
info.M3u8Info[i] = tsCost
|
||||
info.M3u8Info = append(info.M3u8Info, tsCost)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * time.Duration(playlist.Target) * 2)
|
||||
|
@@ -1,8 +1,15 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
. "github.com/langhuihui/monibuca/monica"
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
. "github.com/langhuihui/monibuca/monica"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -11,12 +18,18 @@ const (
|
||||
MSG_VIDEO
|
||||
MSG_SUBSCRIBE
|
||||
MSG_AUTH
|
||||
MSG_SUMMARY
|
||||
MSG_LOG
|
||||
)
|
||||
|
||||
var config = struct {
|
||||
Master string
|
||||
ListenAddr string
|
||||
}{}
|
||||
var (
|
||||
config = struct {
|
||||
Master string
|
||||
ListenAddr string
|
||||
}{}
|
||||
slaves = sync.Map{}
|
||||
masterConn *net.TCPConn
|
||||
)
|
||||
|
||||
func init() {
|
||||
InstallPlugin(&PluginConfig{
|
||||
@@ -29,12 +42,88 @@ func init() {
|
||||
func run() {
|
||||
if config.Master != "" {
|
||||
OnSubscribeHooks.AddHook(onSubscribe)
|
||||
addr, err := net.ResolveTCPAddr("tcp", config.Master)
|
||||
if MayBeError(err) {
|
||||
return
|
||||
}
|
||||
masterConn, err = net.DialTCP("tcp", nil, addr)
|
||||
if MayBeError(err) {
|
||||
return
|
||||
}
|
||||
go readMaster()
|
||||
}
|
||||
if config.ListenAddr != "" {
|
||||
OnSummaryHooks.AddHook(onSummary)
|
||||
log.Printf("server bare start at %s", config.ListenAddr)
|
||||
log.Fatal(ListenBare(config.ListenAddr))
|
||||
}
|
||||
}
|
||||
func readMaster() {
|
||||
var err error
|
||||
defer func() {
|
||||
for {
|
||||
time.Sleep(time.Second*5 + time.Duration(rand.Int63n(5))*time.Second)
|
||||
addr, _ := net.ResolveTCPAddr("tcp", config.Master)
|
||||
if masterConn, err = net.DialTCP("tcp", nil, addr); err == nil {
|
||||
go readMaster()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
brw := bufio.NewReadWriter(bufio.NewReader(masterConn), bufio.NewWriter(masterConn))
|
||||
//首次报告
|
||||
if b, err := json.Marshal(Summary); err == nil {
|
||||
_, err = masterConn.Write(b)
|
||||
}
|
||||
for {
|
||||
cmd, err := brw.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch cmd {
|
||||
case MSG_SUMMARY: //收到主服务器指令,进行采集和上报
|
||||
if cmd, err = brw.ReadByte(); err != nil {
|
||||
return
|
||||
}
|
||||
if cmd == 1 {
|
||||
Summary.Add()
|
||||
go onReport()
|
||||
} else {
|
||||
Summary.Done()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//定时上报
|
||||
func onReport() {
|
||||
for range time.NewTicker(time.Second).C {
|
||||
if Summary.Running() {
|
||||
if b, err := json.Marshal(Summary); err == nil {
|
||||
data := make([]byte, len(b)+2)
|
||||
data[0] = MSG_SUMMARY
|
||||
copy(data[1:], b)
|
||||
data[len(data)-1] = 0
|
||||
_, err = masterConn.Write(data)
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//通知从服务器需要上报或者关闭上报
|
||||
func onSummary(start bool) {
|
||||
slaves.Range(func(k, v interface{}) bool {
|
||||
conn := v.(*net.TCPConn)
|
||||
b := []byte{MSG_SUMMARY, 0}
|
||||
if start {
|
||||
b[1] = 1
|
||||
}
|
||||
conn.Write(b)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func onSubscribe(s *OutputStream) {
|
||||
if s.Publisher == nil {
|
||||
|
@@ -45,6 +45,7 @@ func (p *Receiver) readAVPacket(avType byte) (av *pool.AVPacket, err error) {
|
||||
pool.RecycleSlice(buf)
|
||||
return
|
||||
}
|
||||
|
||||
func PullUpStream(streamPath string) {
|
||||
addr, err := net.ResolveTCPAddr("tcp", config.Master)
|
||||
if MayBeError(err) {
|
||||
|
@@ -3,13 +3,15 @@ package cluster
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
. "github.com/langhuihui/monibuca/monica"
|
||||
"github.com/langhuihui/monibuca/monica/pool"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/langhuihui/monibuca/monica"
|
||||
"github.com/langhuihui/monibuca/monica/pool"
|
||||
)
|
||||
|
||||
func ListenBare(addr string) error {
|
||||
@@ -49,6 +51,7 @@ func ListenBare(addr string) error {
|
||||
func process(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
reader := bufio.NewReader(conn)
|
||||
connAddr := conn.RemoteAddr().String()
|
||||
stream := OutputStream{
|
||||
SendHandler: func(p *pool.SendPacket) error {
|
||||
head := pool.GetSlice(9)
|
||||
@@ -64,7 +67,7 @@ func process(conn net.Conn) {
|
||||
}
|
||||
return nil
|
||||
}, SubscriberInfo: SubscriberInfo{
|
||||
ID: conn.RemoteAddr().String(),
|
||||
ID: connAddr,
|
||||
Type: "Bare",
|
||||
},
|
||||
}
|
||||
@@ -73,31 +76,36 @@ func process(conn net.Conn) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
bytes, err := reader.ReadBytes(0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch cmd {
|
||||
case MSG_SUBSCRIBE:
|
||||
if stream.Room != nil {
|
||||
fmt.Printf("bare stream already exist from %s", conn.RemoteAddr())
|
||||
return
|
||||
}
|
||||
bytes, err := reader.ReadBytes(0)
|
||||
if MayBeError(err) {
|
||||
return
|
||||
}
|
||||
streamName := string(bytes[0 : len(bytes)-1])
|
||||
stream.Play(streamName)
|
||||
go stream.Play(streamName)
|
||||
case MSG_AUTH:
|
||||
bytes, err := reader.ReadBytes(0)
|
||||
if err != nil {
|
||||
print(err)
|
||||
return
|
||||
}
|
||||
sign := strings.Split(string(bytes[0:len(bytes)-1]), ",")
|
||||
head := []byte{MSG_AUTH, 0}
|
||||
head := []byte{MSG_AUTH, 2}
|
||||
if len(sign) > 1 && AuthHooks.Trigger(sign[1]) == nil {
|
||||
head[1] = 1
|
||||
}
|
||||
conn.Write(head)
|
||||
conn.Write(bytes)
|
||||
case MSG_SUMMARY: //收到从服务器发来报告,加入摘要中
|
||||
var summary *ServerSummary
|
||||
if err = json.Unmarshal(bytes, summary); err == nil {
|
||||
summary.Address = connAddr
|
||||
Summary.Report(summary)
|
||||
if _, ok := slaves.Load(connAddr); !ok {
|
||||
slaves.Store(connAddr, conn)
|
||||
defer slaves.Delete(connAddr)
|
||||
}
|
||||
}
|
||||
default:
|
||||
fmt.Printf("bare receive unknown cmd:%d from %s", cmd, conn.RemoteAddr())
|
||||
return
|
||||
|
@@ -3,11 +3,6 @@ package gateway
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
. "github.com/langhuihui/monibuca/monica"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/shirou/gopsutil/net"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime"
|
||||
@@ -16,6 +11,8 @@ import (
|
||||
"path"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
. "github.com/langhuihui/monibuca/monica"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -84,11 +81,34 @@ func init() {
|
||||
})
|
||||
}
|
||||
func run() {
|
||||
http.HandleFunc("/api/stop", stopPublish)
|
||||
http.HandleFunc("/api/summary", summary)
|
||||
http.HandleFunc("/api/logs", watchLogs)
|
||||
http.HandleFunc("/api/config", getConfig)
|
||||
http.HandleFunc("/", website)
|
||||
log.Printf("server gateway start at %s", config.ListenAddr)
|
||||
log.Fatal(http.ListenAndServe(config.ListenAddr, nil))
|
||||
}
|
||||
func getConfig(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(ConfigRaw)
|
||||
}
|
||||
func watchLogs(w http.ResponseWriter, r *http.Request) {
|
||||
AddWriter(NewSSE(w, r.Context()))
|
||||
<-r.Context().Done()
|
||||
}
|
||||
func stopPublish(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"))
|
||||
}
|
||||
}
|
||||
func website(w http.ResponseWriter, r *http.Request) {
|
||||
filePath := r.URL.Path
|
||||
if filePath == "/" {
|
||||
@@ -110,93 +130,19 @@ func website(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
func summary(w http.ResponseWriter, r *http.Request) {
|
||||
sse := NewSSE(w, r.Context())
|
||||
s := collect()
|
||||
sse.WriteJSON(&s)
|
||||
for range time.NewTicker(time.Second).C {
|
||||
old := s
|
||||
s = collect()
|
||||
for i, v := range s.NetWork {
|
||||
s.NetWork[i].ReceiveSpeed = v.Receive - old.NetWork[i].Receive
|
||||
s.NetWork[i].SentSpeed = v.Sent - old.NetWork[i].Sent
|
||||
}
|
||||
AllRoom.Range(func(key interface{}, v interface{}) bool {
|
||||
s.Rooms = append(s.Rooms, &v.(*Room).RoomInfo)
|
||||
return true
|
||||
})
|
||||
if sse.WriteJSON(&s) != nil {
|
||||
break
|
||||
Summary.Add()
|
||||
defer Summary.Done()
|
||||
sse.WriteJSON(&Summary)
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if sse.WriteJSON(&Summary) != nil {
|
||||
return
|
||||
}
|
||||
case <-r.Context().Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Summary struct {
|
||||
Memory struct {
|
||||
Total uint64
|
||||
Free uint64
|
||||
Used uint64
|
||||
Usage float64
|
||||
}
|
||||
CPUUsage float64
|
||||
HardDisk struct {
|
||||
Total uint64
|
||||
Free uint64
|
||||
Used uint64
|
||||
Usage float64
|
||||
}
|
||||
NetWork []NetWorkInfo
|
||||
Rooms []*RoomInfo
|
||||
}
|
||||
type NetWorkInfo struct {
|
||||
Name string
|
||||
Receive uint64
|
||||
Sent uint64
|
||||
ReceiveSpeed uint64
|
||||
SentSpeed uint64
|
||||
}
|
||||
|
||||
func collect() (s Summary) {
|
||||
v, _ := mem.VirtualMemory()
|
||||
//c, _ := cpu.Info()
|
||||
cc, _ := cpu.Percent(time.Second, false)
|
||||
d, _ := disk.Usage("/")
|
||||
//n, _ := host.Info()
|
||||
nv, _ := net.IOCounters(true)
|
||||
//boottime, _ := host.BootTime()
|
||||
//btime := time.Unix(int64(boottime), 0).Format("2006-01-02 15:04:05")
|
||||
s.Memory.Total = v.Total / 1024 / 1024
|
||||
s.Memory.Free = v.Available / 1024 / 1024
|
||||
s.Memory.Used = v.Used / 1024 / 1024
|
||||
s.Memory.Usage = v.UsedPercent
|
||||
//fmt.Printf(" Mem : %v MB Free: %v MB Used:%v Usage:%f%%\n", v.Total/1024/1024, v.Available/1024/1024, v.Used/1024/1024, v.UsedPercent)
|
||||
//if len(c) > 1 {
|
||||
// for _, sub_cpu := range c {
|
||||
// modelname := sub_cpu.ModelName
|
||||
// cores := sub_cpu.Cores
|
||||
// fmt.Printf(" CPU : %v %v cores \n", modelname, cores)
|
||||
// }
|
||||
//} else {
|
||||
// sub_cpu := c[0]
|
||||
// modelname := sub_cpu.ModelName
|
||||
// cores := sub_cpu.Cores
|
||||
// fmt.Printf(" CPU : %v %v cores \n", modelname, cores)
|
||||
//}
|
||||
s.CPUUsage = cc[0]
|
||||
s.HardDisk.Free = d.Free / 1024 / 1024 / 1024
|
||||
s.HardDisk.Total = d.Total / 1024 / 1024 / 1024
|
||||
s.HardDisk.Used = d.Used / 1024 / 1024 / 1024
|
||||
s.HardDisk.Usage = d.UsedPercent
|
||||
s.NetWork = make([]NetWorkInfo, len(nv))
|
||||
for i, n := range nv {
|
||||
s.NetWork[i].Name = n.Name
|
||||
s.NetWork[i].Receive = n.BytesRecv
|
||||
s.NetWork[i].Sent = n.BytesSent
|
||||
}
|
||||
|
||||
//fmt.Printf(" Network: %v bytes / %v bytes\n", nv[0].BytesRecv, nv[0].BytesSent)
|
||||
//fmt.Printf(" SystemBoot:%v\n", btime)
|
||||
//fmt.Printf(" CPU Used : used %f%% \n", cc[0])
|
||||
//fmt.Printf(" HD : %v GB Free: %v GB Usage:%f%%\n", d.Total/1024/1024/1024, d.Free/1024/1024/1024, d.UsedPercent)
|
||||
//fmt.Printf(" OS : %v(%v) %v \n", n.Platform, n.PlatformFamily, n.PlatformVersion)
|
||||
//fmt.Printf(" Hostname : %v \n", n.Hostname)
|
||||
return
|
||||
}
|
||||
|
@@ -7,8 +7,26 @@ import (
|
||||
"github.com/langhuihui/monibuca/monica/util"
|
||||
"io"
|
||||
"os"
|
||||
"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 {
|
||||
@@ -17,37 +35,30 @@ func SaveFlv(streamPath string, append bool) error {
|
||||
flag = flag | os.O_TRUNC | os.O_WRONLY
|
||||
}
|
||||
filePath := config.Path + streamPath + ".flv"
|
||||
os.MkdirAll(path.Dir(filePath), 0666)
|
||||
file, err := os.OpenFile(filePath, flag, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
p := OutputStream{SendHandler: func(packet *pool.SendPacket) error {
|
||||
return avformat.WriteFLVTag(file, packet)
|
||||
}}
|
||||
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)
|
||||
}
|
||||
if err == nil {
|
||||
recordings.Store(filePath, &p)
|
||||
go p.Play(streamPath)
|
||||
go func() {
|
||||
p.Play(streamPath)
|
||||
file.Close()
|
||||
}()
|
||||
} else {
|
||||
file.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@@ -1,9 +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 {
|
||||
@@ -11,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",
|
||||
@@ -20,6 +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 {
|
||||
@@ -31,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 != "" {
|
||||
@@ -46,4 +74,85 @@ func run() {
|
||||
w.Write([]byte("no such stream"))
|
||||
}
|
||||
})
|
||||
http.HandleFunc("/api/record/flv/play", func(w http.ResponseWriter, r *http.Request) {
|
||||
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
||||
if err := PublishFlvFile(streamPath); err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
} else {
|
||||
w.Write([]byte("success"))
|
||||
}
|
||||
} else {
|
||||
w.Write([]byte("no streamPath"))
|
||||
}
|
||||
})
|
||||
http.HandleFunc("/api/record/flv/delete", func(w http.ResponseWriter, r *http.Request) {
|
||||
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
||||
filePath := config.Path + streamPath + ".flv"
|
||||
if PathExists(filePath) {
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
w.Write([]byte(err.Error()))
|
||||
} else {
|
||||
w.Write([]byte("success"))
|
||||
}
|
||||
} else {
|
||||
w.Write([]byte("no such file"))
|
||||
}
|
||||
} else {
|
||||
w.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
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -87,6 +87,9 @@ func processRtmp(conn net.Conn) {
|
||||
}
|
||||
switch msg.MessageTypeID {
|
||||
case RTMP_MSG_AMF0_COMMAND:
|
||||
if msg.MsgData == nil {
|
||||
break
|
||||
}
|
||||
cmd := msg.MsgData.(Commander).GetCommand()
|
||||
switch cmd.CommandName {
|
||||
case "createStream":
|
||||
|
Reference in New Issue
Block a user