This commit is contained in:
李宇翔
2020-04-17 14:47:00 +08:00
parent daea3301a1
commit f52662249e
22 changed files with 1347 additions and 1244 deletions

View File

@@ -1,8 +0,0 @@
<meta charset="utf-8">
<title>plugin-record demo</title>
<script src="https://unpkg.com/vue"></script>
<script src="./plugin-record.js"></script>
<plugin-record></plugin-record>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
flv.go
View File

@@ -1,4 +1,4 @@
package recordplugin
package record
import (
"io"

4
go.mod
View File

@@ -1,5 +1,5 @@
module github.com/Monibuca/recordplugin
module github.com/Monibuca/plugin-record
go 1.13
require github.com/Monibuca/engine v1.1.1
require github.com/Monibuca/engine v1.2.1

20
go.sum
View File

@@ -2,19 +2,39 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Monibuca/engine v1.0.3 h1:/xwiEpqgQDAzjDb6zAwqFSZP8y0MOOVXv5aUx1os5nM=
github.com/Monibuca/engine v1.0.3/go.mod h1:NjqVgtXuRSOyk3+NWgCuDf2p7TsBisjYxoEwA9uCZ38=
github.com/Monibuca/engine v1.1.1 h1:wk5qBJCssCOpeA7jyGU/oInVmFnfc9r+eT+sbjOIZfw=
github.com/Monibuca/engine v1.1.1/go.mod h1:NjqVgtXuRSOyk3+NWgCuDf2p7TsBisjYxoEwA9uCZ38=
github.com/Monibuca/engine v1.2.1 h1:TJmC6eZA1lR1MScWgempZLiEZD4T6aY/nn/rlQ9UdK8=
github.com/Monibuca/engine v1.2.1/go.mod h1:WbDkXENLjcPjyjCR1Mix1GA+uAlwORkv/+8aMVrDX2g=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478 h1:Db9StoJ6RZN3YttC0Pm0I4Y5izITRYch3RMbT59BYN0=
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478/go.mod h1:0j1+svBH8ABEIPdUP0AIg4qedsybnXGJBakCEw8cfoo=
github.com/funny/utest v0.0.0-20161029064919-43870a374500 h1:Z0r1CZnoIWFB/Uiwh1BU5FYmuFe6L5NPi6XWQEmsTRg=
github.com/funny/utest v0.0.0-20161029064919-43870a374500/go.mod h1:mUn39tBov9jKnTWV1RlOYoNzxdBFHiSzXWdY1FoNGGg=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/shirou/gopsutil v2.20.1+incompatible h1:oIq9Cq4i84Hk8uQAUOG3eNdI/29hBawGrD5YRl6JRDY=
github.com/shirou/gopsutil v2.20.1+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -1,4 +1,4 @@
package recordplugin
package record
import (
"encoding/json"

View File

@@ -1,4 +1,4 @@
package recordplugin
package record
import (
"errors"

19
ui/dist/demo.html vendored Normal file
View File

@@ -0,0 +1,19 @@
<meta charset="utf-8">
<title>plugin-record demo</title>
<script src="https://unpkg.com/vue"></script>
<script src="./plugin-record.umd.js"></script>
<link rel="stylesheet" href="./plugin-record.css">
<div id="app">
<demo></demo>
</div>
<script>
new Vue({
components: {
demo: plugin-record
}
}).$mount('#app')
</script>

618
ui/dist/plugin-record.common.js vendored Normal file
View File

@@ -0,0 +1,618 @@
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "fb15");
/******/ })
/************************************************************************/
/******/ ({
/***/ "034f":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("85ec");
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_0__);
/* unused harmony reexport * */
/* unused harmony default export */ var _unused_webpack_default_export = (_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_0___default.a);
/***/ }),
/***/ "0e05":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Records_vue_vue_type_style_index_0_id_7c800264_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("a189");
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Records_vue_vue_type_style_index_0_id_7c800264_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Records_vue_vue_type_style_index_0_id_7c800264_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0__);
/* unused harmony reexport * */
/* unused harmony default export */ var _unused_webpack_default_export = (_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Records_vue_vue_type_style_index_0_id_7c800264_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0___default.a);
/***/ }),
/***/ "85ec":
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/***/ "a189":
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/***/ "f6fd":
/***/ (function(module, exports) {
// document.currentScript polyfill by Adam Miller
// MIT license
(function(document){
var currentScript = "currentScript",
scripts = document.getElementsByTagName('script'); // Live NodeList collection
// If browser needs currentScript polyfill, add get currentScript() to the document object
if (!(currentScript in document)) {
Object.defineProperty(document, currentScript, {
get: function(){
// IE 6-10 supports script readyState
// IE 10+ support stack trace
try { throw new Error(); }
catch (err) {
// Find the second match for the "at" string to get file src url from stack.
// Specifically works with the format of stack traces in IE.
var i, res = ((/.*at [^\(]*\((.*):.+:.+\)$/ig).exec(err.stack) || [false])[1];
// For all scripts on the page, if src matches or if ready state is interactive, return the script tag
for(i in scripts){
if(scripts[i].src == res || scripts[i].readyState == "interactive"){
return scripts[i];
}
}
// If no match, return null
return null;
}
}
});
}
})(document);
/***/ }),
/***/ "fb15":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js
// This file is imported into lib/wc client bundles.
if (typeof window !== 'undefined') {
if (true) {
__webpack_require__("f6fd")
}
var i
if ((i = window.document.currentScript) && (i = i.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))) {
__webpack_require__.p = i[1] // eslint-disable-line
}
}
// Indicate to webpack that this file can be concatenated
/* harmony default export */ var setPublicPath = (null);
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"5b04a8fd-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=655db994&
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-tabs',{attrs:{"value":_vm.active1,"indicator-color":"#80deea","inverse":"","center":""},on:{"update:value":function($event){_vm.active1=$event}}},[_c('mu-tab',[_vm._v("直播流")]),_c('mu-tab',[_vm._v("录制的视频")])],1),(_vm.Rooms.length==0 && _vm.active1==0)?_c('div',{staticClass:"empty"},[_c('Icon',{attrs:{"type":"md-wine","size":"50"}}),_vm._v("没有任何房间 ")],1):(_vm.active1==0)?_vm._l((_vm.Rooms),function(item){return _c('mu-card',{key:item.StreamPath,staticClass:"room"},[_c('mu-card-title',{attrs:{"title":item.StreamPath,"sub-title":item.StartTime}}),_c('mu-card-text',[_c('p',[_vm._v(" "+_vm._s(_vm.SoundFormat(item.AudioInfo.SoundFormat))+" "+_vm._s(item.AudioInfo.PacketCount)+" "+_vm._s(_vm.SoundRate(item.AudioInfo.SoundRate))+" 声道:"+_vm._s(item.AudioInfo.SoundType)+" ")]),_c('p',[_vm._v(" "+_vm._s(_vm.CodecID(item.VideoInfo.CodecID))+" "+_vm._s(item.VideoInfo.PacketCount)+" "+_vm._s(item.VideoInfo.SPSInfo.Width)+"x"+_vm._s(item.VideoInfo.SPSInfo.Height)+" ")])]),_c('mu-card-actions',[(_vm.isRecording(item))?_c('mu-button',{staticClass:"recording",attrs:{"icon":""},on:{"click":function($event){return _vm.stopRecord(item)}}},[_c('mu-icon',{attrs:{"value":"fiber_manual_record"}})],1):_c('mu-button',{attrs:{"icon":""},on:{"click":function($event){return _vm.record(item)}}},[_c('mu-icon',{attrs:{"value":"fiber_manual_record"}})],1)],1)],1)}):_vm._e(),(_vm.active1==1)?_c('Records',{ref:"recordsPanel"}):_vm._e()],2)}
var staticRenderFns = []
// CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=655db994&
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"5b04a8fd-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Records.vue?vue&type=template&id=7c800264&scoped=true&
var Recordsvue_type_template_id_7c800264_scoped_true_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"records"},_vm._l((_vm.data),function(item){return _c('mu-card',{key:item},[_c('mu-card-title',{attrs:{"title":item.Path,"sub-title":_vm.unitFormat(item.Size)+' '+_vm.toDurationStr(item.Duration)}}),_c('mu-card-actions',[_c('mu-button',{attrs:{"icon":"","small":""},on:{"click":function($event){return _vm.play(item)}}},[_c('mu-icon',{attrs:{"value":"play_arrow"}})],1),_c('mu-button',{attrs:{"icon":"","small":""},on:{"click":function($event){return _vm.deleteFlv(item)}}},[_c('mu-icon',{attrs:{"value":"delete_forever"}})],1)],1)],1)}),1)}
var Recordsvue_type_template_id_7c800264_scoped_true_staticRenderFns = []
// CONCATENATED MODULE: ./src/components/Records.vue?vue&type=template&id=7c800264&scoped=true&
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Records.vue?vue&type=script&lang=js&
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
/* harmony default export */ var Recordsvue_type_script_lang_js_ = ({
data() {
return {
data: []
};
},
methods: {
play(item) {
this.ajax.get(
"/record/flv/play",
{ streamPath: item.Path.replace(".flv", "") },
x => {
if (x == "success") {
this.onVisible(true);
this.$toast.success("开始发布");
} else {
this.$toast.error(x);
}
}
);
},
deleteFlv(item) {
this.$confirm("是否删除Flv文件", "提示").then(result => {
if (result) {
return this.ajax.get(
"/record/flv/delete",
{ streamPath: item.Path.replace(".flv", "") },
x => {
if (x == "success") {
this.$toast.success("删除成功");
} else {
this.$toast.error(x);
}
}
);
}
});
},
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) {
this.ajax.getJSON("/record/flv/list", {}, x => {
this.data = x;
});
}
}
}
});
// CONCATENATED MODULE: ./src/components/Records.vue?vue&type=script&lang=js&
/* harmony default export */ var components_Recordsvue_type_script_lang_js_ = (Recordsvue_type_script_lang_js_);
// EXTERNAL MODULE: ./src/components/Records.vue?vue&type=style&index=0&id=7c800264&scoped=true&lang=css&
var Recordsvue_type_style_index_0_id_7c800264_scoped_true_lang_css_ = __webpack_require__("0e05");
// CONCATENATED MODULE: ./node_modules/vue-loader/lib/runtime/componentNormalizer.js
/* globals __VUE_SSR_CONTEXT__ */
// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
// This module is a runtime utility for cleaner component module output and will
// be included in the final webpack user bundle.
function normalizeComponent (
scriptExports,
render,
staticRenderFns,
functionalTemplate,
injectStyles,
scopeId,
moduleIdentifier, /* server only */
shadowMode /* vue-cli only */
) {
// Vue.extend constructor export interop
var options = typeof scriptExports === 'function'
? scriptExports.options
: scriptExports
// render functions
if (render) {
options.render = render
options.staticRenderFns = staticRenderFns
options._compiled = true
}
// functional template
if (functionalTemplate) {
options.functional = true
}
// scopedId
if (scopeId) {
options._scopeId = 'data-v-' + scopeId
}
var hook
if (moduleIdentifier) { // server build
hook = function (context) {
// 2.3 injection
context =
context || // cached call
(this.$vnode && this.$vnode.ssrContext) || // stateful
(this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__
}
// inject component styles
if (injectStyles) {
injectStyles.call(this, context)
}
// register component module identifier for async chunk inferrence
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier)
}
}
// used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook
} else if (injectStyles) {
hook = shadowMode
? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }
: injectStyles
}
if (hook) {
if (options.functional) {
// for template-only hot-reload because in that case the render fn doesn't
// go through the normalizer
options._injectStyles = hook
// register for functional component in vue file
var originalRender = options.render
options.render = function renderWithStyleInjection (h, context) {
hook.call(context)
return originalRender(h, context)
}
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate
options.beforeCreate = existing
? [].concat(existing, hook)
: [hook]
}
}
return {
exports: scriptExports,
options: options
}
}
// CONCATENATED MODULE: ./src/components/Records.vue
/* normalize component */
var component = normalizeComponent(
components_Recordsvue_type_script_lang_js_,
Recordsvue_type_template_id_7c800264_scoped_true_render,
Recordsvue_type_template_id_7c800264_scoped_true_staticRenderFns,
false,
null,
"7c800264",
null
)
/* harmony default export */ var Records = (component.exports);
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js&
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
let roomsES = 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"
};
/* harmony default export */ var Appvue_type_script_lang_js_ = ({
components: {
Records: Records
},
data() {
return {
Rooms: [],
typeMap: {
Receiver: "📡",
FlvFile: "🎥",
TS: "🎬",
HLS: "🍎",
"": "⏳",
Match365: "🏆",
RTMP: "🚠"
}
};
},
methods: {
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(
"/record/flv?append=true",
{ streamPath: item.StreamPath },
x => {
if (x == "success") {
this.$Message.success("开始录制(追加模式)");
} else {
this.$Message.error(x);
}
}
);
},
onCancel: () => {
window.ajax.get(
"/record/flv",
{ streamPath: item.StreamPath },
x => {
if (x == "success") {
this.$Message.success("开始录制");
} else {
this.$Message.error(x);
}
}
);
}
});
},
stopRecord(item) {
window.ajax.get(
"/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")
);
},
fetchRooms() {
roomsES = new EventSource("/api/summary");
roomsES.onmessage = evt => {
if (!evt.data) return;
let summary = JSON.parse(evt.data);
this.Rooms = (summary && summary.Rooms) || [];
this.Rooms.sort((a, b) =>
a.StreamPath > b.StreamPath ? 1 : -1
);
};
},
onClickTab(name) {
this.$refs.recordsPanel.onVisible(name == "recordsPanel");
}
},
mounted() {
this.fetchRooms();
},
destroyed() {
roomsES.close();
}
});
// CONCATENATED MODULE: ./src/App.vue?vue&type=script&lang=js&
/* harmony default export */ var src_Appvue_type_script_lang_js_ = (Appvue_type_script_lang_js_);
// EXTERNAL MODULE: ./src/App.vue?vue&type=style&index=0&lang=css&
var Appvue_type_style_index_0_lang_css_ = __webpack_require__("034f");
// CONCATENATED MODULE: ./src/App.vue
/* normalize component */
var App_component = normalizeComponent(
src_Appvue_type_script_lang_js_,
render,
staticRenderFns,
false,
null,
null,
null
)
/* harmony default export */ var App = (App_component.exports);
// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/entry-lib.js
/* harmony default export */ var entry_lib = __webpack_exports__["default"] = (App);
/***/ })
/******/ })["default"];
//# sourceMappingURL=plugin-record.common.js.map

1
ui/dist/plugin-record.common.js.map vendored Normal file

File diff suppressed because one or more lines are too long

1
ui/dist/plugin-record.css vendored Normal file
View File

@@ -0,0 +1 @@
.records[data-v-7c800264]{display:flex;flex-wrap:wrap;padding:0 15px}.records>[data-v-7c800264]{width:200px}@-webkit-keyframes recording{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}@keyframes recording{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}.recording{-webkit-animation:recording 1s infinite;animation:recording 1s infinite}.layout{padding-bottom:30px;display:flex;flex-wrap:wrap}.room{width:250px;margin:10px;text-align:left}.empty{color:#ffc107;width:100%;min-height:500px;display:flex;justify-content:center;align-items:center}.status{position:fixed;display:flex;left:5px;bottom:10px}.status>div{margin:0 5px}

628
ui/dist/plugin-record.umd.js vendored Normal file
View File

@@ -0,0 +1,628 @@
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["plugin-record"] = factory();
else
root["plugin-record"] = factory();
})((typeof self !== 'undefined' ? self : this), function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "fb15");
/******/ })
/************************************************************************/
/******/ ({
/***/ "034f":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("85ec");
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_0__);
/* unused harmony reexport * */
/* unused harmony default export */ var _unused_webpack_default_export = (_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_lang_css___WEBPACK_IMPORTED_MODULE_0___default.a);
/***/ }),
/***/ "0e05":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Records_vue_vue_type_style_index_0_id_7c800264_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("a189");
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Records_vue_vue_type_style_index_0_id_7c800264_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Records_vue_vue_type_style_index_0_id_7c800264_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0__);
/* unused harmony reexport * */
/* unused harmony default export */ var _unused_webpack_default_export = (_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_Records_vue_vue_type_style_index_0_id_7c800264_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0___default.a);
/***/ }),
/***/ "85ec":
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/***/ "a189":
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/***/ "f6fd":
/***/ (function(module, exports) {
// document.currentScript polyfill by Adam Miller
// MIT license
(function(document){
var currentScript = "currentScript",
scripts = document.getElementsByTagName('script'); // Live NodeList collection
// If browser needs currentScript polyfill, add get currentScript() to the document object
if (!(currentScript in document)) {
Object.defineProperty(document, currentScript, {
get: function(){
// IE 6-10 supports script readyState
// IE 10+ support stack trace
try { throw new Error(); }
catch (err) {
// Find the second match for the "at" string to get file src url from stack.
// Specifically works with the format of stack traces in IE.
var i, res = ((/.*at [^\(]*\((.*):.+:.+\)$/ig).exec(err.stack) || [false])[1];
// For all scripts on the page, if src matches or if ready state is interactive, return the script tag
for(i in scripts){
if(scripts[i].src == res || scripts[i].readyState == "interactive"){
return scripts[i];
}
}
// If no match, return null
return null;
}
}
});
}
})(document);
/***/ }),
/***/ "fb15":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js
// This file is imported into lib/wc client bundles.
if (typeof window !== 'undefined') {
if (true) {
__webpack_require__("f6fd")
}
var i
if ((i = window.document.currentScript) && (i = i.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))) {
__webpack_require__.p = i[1] // eslint-disable-line
}
}
// Indicate to webpack that this file can be concatenated
/* harmony default export */ var setPublicPath = (null);
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"5b04a8fd-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=655db994&
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-tabs',{attrs:{"value":_vm.active1,"indicator-color":"#80deea","inverse":"","center":""},on:{"update:value":function($event){_vm.active1=$event}}},[_c('mu-tab',[_vm._v("直播流")]),_c('mu-tab',[_vm._v("录制的视频")])],1),(_vm.Rooms.length==0 && _vm.active1==0)?_c('div',{staticClass:"empty"},[_c('Icon',{attrs:{"type":"md-wine","size":"50"}}),_vm._v("没有任何房间 ")],1):(_vm.active1==0)?_vm._l((_vm.Rooms),function(item){return _c('mu-card',{key:item.StreamPath,staticClass:"room"},[_c('mu-card-title',{attrs:{"title":item.StreamPath,"sub-title":item.StartTime}}),_c('mu-card-text',[_c('p',[_vm._v(" "+_vm._s(_vm.SoundFormat(item.AudioInfo.SoundFormat))+" "+_vm._s(item.AudioInfo.PacketCount)+" "+_vm._s(_vm.SoundRate(item.AudioInfo.SoundRate))+" 声道:"+_vm._s(item.AudioInfo.SoundType)+" ")]),_c('p',[_vm._v(" "+_vm._s(_vm.CodecID(item.VideoInfo.CodecID))+" "+_vm._s(item.VideoInfo.PacketCount)+" "+_vm._s(item.VideoInfo.SPSInfo.Width)+"x"+_vm._s(item.VideoInfo.SPSInfo.Height)+" ")])]),_c('mu-card-actions',[(_vm.isRecording(item))?_c('mu-button',{staticClass:"recording",attrs:{"icon":""},on:{"click":function($event){return _vm.stopRecord(item)}}},[_c('mu-icon',{attrs:{"value":"fiber_manual_record"}})],1):_c('mu-button',{attrs:{"icon":""},on:{"click":function($event){return _vm.record(item)}}},[_c('mu-icon',{attrs:{"value":"fiber_manual_record"}})],1)],1)],1)}):_vm._e(),(_vm.active1==1)?_c('Records',{ref:"recordsPanel"}):_vm._e()],2)}
var staticRenderFns = []
// CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=655db994&
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"5b04a8fd-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Records.vue?vue&type=template&id=7c800264&scoped=true&
var Recordsvue_type_template_id_7c800264_scoped_true_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"records"},_vm._l((_vm.data),function(item){return _c('mu-card',{key:item},[_c('mu-card-title',{attrs:{"title":item.Path,"sub-title":_vm.unitFormat(item.Size)+' '+_vm.toDurationStr(item.Duration)}}),_c('mu-card-actions',[_c('mu-button',{attrs:{"icon":"","small":""},on:{"click":function($event){return _vm.play(item)}}},[_c('mu-icon',{attrs:{"value":"play_arrow"}})],1),_c('mu-button',{attrs:{"icon":"","small":""},on:{"click":function($event){return _vm.deleteFlv(item)}}},[_c('mu-icon',{attrs:{"value":"delete_forever"}})],1)],1)],1)}),1)}
var Recordsvue_type_template_id_7c800264_scoped_true_staticRenderFns = []
// CONCATENATED MODULE: ./src/components/Records.vue?vue&type=template&id=7c800264&scoped=true&
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Records.vue?vue&type=script&lang=js&
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
/* harmony default export */ var Recordsvue_type_script_lang_js_ = ({
data() {
return {
data: []
};
},
methods: {
play(item) {
this.ajax.get(
"/record/flv/play",
{ streamPath: item.Path.replace(".flv", "") },
x => {
if (x == "success") {
this.onVisible(true);
this.$toast.success("开始发布");
} else {
this.$toast.error(x);
}
}
);
},
deleteFlv(item) {
this.$confirm("是否删除Flv文件", "提示").then(result => {
if (result) {
return this.ajax.get(
"/record/flv/delete",
{ streamPath: item.Path.replace(".flv", "") },
x => {
if (x == "success") {
this.$toast.success("删除成功");
} else {
this.$toast.error(x);
}
}
);
}
});
},
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) {
this.ajax.getJSON("/record/flv/list", {}, x => {
this.data = x;
});
}
}
}
});
// CONCATENATED MODULE: ./src/components/Records.vue?vue&type=script&lang=js&
/* harmony default export */ var components_Recordsvue_type_script_lang_js_ = (Recordsvue_type_script_lang_js_);
// EXTERNAL MODULE: ./src/components/Records.vue?vue&type=style&index=0&id=7c800264&scoped=true&lang=css&
var Recordsvue_type_style_index_0_id_7c800264_scoped_true_lang_css_ = __webpack_require__("0e05");
// CONCATENATED MODULE: ./node_modules/vue-loader/lib/runtime/componentNormalizer.js
/* globals __VUE_SSR_CONTEXT__ */
// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
// This module is a runtime utility for cleaner component module output and will
// be included in the final webpack user bundle.
function normalizeComponent (
scriptExports,
render,
staticRenderFns,
functionalTemplate,
injectStyles,
scopeId,
moduleIdentifier, /* server only */
shadowMode /* vue-cli only */
) {
// Vue.extend constructor export interop
var options = typeof scriptExports === 'function'
? scriptExports.options
: scriptExports
// render functions
if (render) {
options.render = render
options.staticRenderFns = staticRenderFns
options._compiled = true
}
// functional template
if (functionalTemplate) {
options.functional = true
}
// scopedId
if (scopeId) {
options._scopeId = 'data-v-' + scopeId
}
var hook
if (moduleIdentifier) { // server build
hook = function (context) {
// 2.3 injection
context =
context || // cached call
(this.$vnode && this.$vnode.ssrContext) || // stateful
(this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__
}
// inject component styles
if (injectStyles) {
injectStyles.call(this, context)
}
// register component module identifier for async chunk inferrence
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier)
}
}
// used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook
} else if (injectStyles) {
hook = shadowMode
? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }
: injectStyles
}
if (hook) {
if (options.functional) {
// for template-only hot-reload because in that case the render fn doesn't
// go through the normalizer
options._injectStyles = hook
// register for functional component in vue file
var originalRender = options.render
options.render = function renderWithStyleInjection (h, context) {
hook.call(context)
return originalRender(h, context)
}
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate
options.beforeCreate = existing
? [].concat(existing, hook)
: [hook]
}
}
return {
exports: scriptExports,
options: options
}
}
// CONCATENATED MODULE: ./src/components/Records.vue
/* normalize component */
var component = normalizeComponent(
components_Recordsvue_type_script_lang_js_,
Recordsvue_type_template_id_7c800264_scoped_true_render,
Recordsvue_type_template_id_7c800264_scoped_true_staticRenderFns,
false,
null,
"7c800264",
null
)
/* harmony default export */ var Records = (component.exports);
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js&
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
let roomsES = 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"
};
/* harmony default export */ var Appvue_type_script_lang_js_ = ({
components: {
Records: Records
},
data() {
return {
Rooms: [],
typeMap: {
Receiver: "📡",
FlvFile: "🎥",
TS: "🎬",
HLS: "🍎",
"": "⏳",
Match365: "🏆",
RTMP: "🚠"
}
};
},
methods: {
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(
"/record/flv?append=true",
{ streamPath: item.StreamPath },
x => {
if (x == "success") {
this.$Message.success("开始录制(追加模式)");
} else {
this.$Message.error(x);
}
}
);
},
onCancel: () => {
window.ajax.get(
"/record/flv",
{ streamPath: item.StreamPath },
x => {
if (x == "success") {
this.$Message.success("开始录制");
} else {
this.$Message.error(x);
}
}
);
}
});
},
stopRecord(item) {
window.ajax.get(
"/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")
);
},
fetchRooms() {
roomsES = new EventSource("/api/summary");
roomsES.onmessage = evt => {
if (!evt.data) return;
let summary = JSON.parse(evt.data);
this.Rooms = (summary && summary.Rooms) || [];
this.Rooms.sort((a, b) =>
a.StreamPath > b.StreamPath ? 1 : -1
);
};
},
onClickTab(name) {
this.$refs.recordsPanel.onVisible(name == "recordsPanel");
}
},
mounted() {
this.fetchRooms();
},
destroyed() {
roomsES.close();
}
});
// CONCATENATED MODULE: ./src/App.vue?vue&type=script&lang=js&
/* harmony default export */ var src_Appvue_type_script_lang_js_ = (Appvue_type_script_lang_js_);
// EXTERNAL MODULE: ./src/App.vue?vue&type=style&index=0&lang=css&
var Appvue_type_style_index_0_lang_css_ = __webpack_require__("034f");
// CONCATENATED MODULE: ./src/App.vue
/* normalize component */
var App_component = normalizeComponent(
src_Appvue_type_script_lang_js_,
render,
staticRenderFns,
false,
null,
null,
null
)
/* harmony default export */ var App = (App_component.exports);
// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/entry-lib.js
/* harmony default export */ var entry_lib = __webpack_exports__["default"] = (App);
/***/ })
/******/ })["default"];
});
//# sourceMappingURL=plugin-record.umd.js.map

1
ui/dist/plugin-record.umd.js.map vendored Normal file

File diff suppressed because one or more lines are too long

2
ui/dist/plugin-record.umd.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
ui/dist/plugin-record.umd.min.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@
"description": "dashboard of record plugin for monibuca",
"main": "index.js",
"scripts": {
"build": "vue-cli-service build --dest ui --target wc --name plugin-record index.vue"
"build": "vue-cli-service build --target lib --name plugin-record"
},
"author": "dexter",
"license": "ISC",
@@ -12,4 +12,4 @@
"@vue/cli-service": "^4.2.3",
"vue-template-compiler": "^2.6.11"
}
}
}

View File

@@ -1,9 +1,16 @@
<template>
<Tabs value="liveStream" @on-click="onClickTab" type="card">
<TabPane label="直播流" icon="md-videocam" name="liveStream" 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>
<div>
<mu-tabs :value.sync="active1" indicator-color="#80deea" inverse center>
<mu-tab>直播流</mu-tab>
<mu-tab>录制的视频</mu-tab>
</mu-tabs>
<div v-if="Rooms.length==0 && active1==0" class="empty">
<Icon type="md-wine" size="50" />没有任何房间
</div>
<template v-else-if="active1==0">
<mu-card v-for="item in Rooms" :key="item.StreamPath" class="room">
<mu-card-title :title="item.StreamPath" :sub-title="item.StartTime" />
<mu-card-text>
<p>
{{SoundFormat(item.AudioInfo.SoundFormat)}} {{item.AudioInfo.PacketCount}}
{{SoundRate(item.AudioInfo.SoundRate)}} 声道:{{item.AudioInfo.SoundType}}
@@ -12,24 +19,19 @@
{{CodecID(item.VideoInfo.CodecID)}} {{item.VideoInfo.PacketCount}}
{{item.VideoInfo.SPSInfo.Width}}x{{item.VideoInfo.SPSInfo.Height}}
</p>
<template slot="extra">
<Button size="small"
@click="stopRecord(item)"
class="recording"
v-if="isRecording(item)"
icon="ios-radio-button-on"
></Button>
<Button size="small" @click="record(item)" v-else icon="ios-radio-button-on"></Button>
</template>
</Card>
<div v-if="Rooms.length==0" class="empty">
<Icon type="md-wine" size="50" />没有任何房间
</div>
</TabPane>
<TabPane label="录制的视频" icon="ios-folder" name="recordsPanel">
<Records ref="recordsPanel" />
</TabPane>
</Tabs>
</mu-card-text>
<mu-card-actions>
<mu-button icon @click="stopRecord(item)" class="recording" v-if="isRecording(item)">
<mu-icon value="fiber_manual_record" />
</mu-button>
<mu-button icon @click="record(item)" v-else>
<mu-icon value="fiber_manual_record" />
</mu-button>
</mu-card-actions>
</mu-card>
</template>
<Records ref="recordsPanel" v-if="active1==1" />
</div>
</template>
<script>
@@ -152,8 +154,8 @@ export default {
);
};
},
onClickTab(name){
this.$refs.recordsPanel.onVisible(name=="recordsPanel")
onClickTab(name) {
this.$refs.recordsPanel.onVisible(name == "recordsPanel");
}
},
mounted() {
@@ -166,7 +168,6 @@ export default {
</script>
<style>
@import url("/iview.css");
@keyframes recording {
0% {
opacity: 0.2;
@@ -196,7 +197,7 @@ export default {
}
.empty {
color: #eb5e46;
color: #ffc107;
width: 100%;
min-height: 500px;
display: flex;

View File

@@ -1,13 +1,16 @@
<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>
{{unitFormat(item.Size)}} {{toDurationStr(item.Duration)}}
</Card>
<mu-card v-for="item in data" :key="item">
<mu-card-title :title="item.Path" :sub-title="unitFormat(item.Size)+' '+toDurationStr(item.Duration)" />
<mu-card-actions>
<mu-button @click="play(item)" icon small>
<mu-icon value="play_arrow" />
</mu-button>
<mu-button @click="deleteFlv(item)" icon small>
<mu-icon value="delete_forever" />
</mu-button>
</mu-card-actions>
</mu-card>
</div>
</template>
@@ -20,40 +23,36 @@ export default {
},
methods: {
play(item) {
window.ajax.get(
this.ajax.get(
"/record/flv/play",
{ streamPath: item.Path.replace(".flv", "") },
x => {
if (x == "success") {
this.onVisible(true);
this.$Message.success("开始发布");
this.$toast.success("开始发布");
} else {
this.$Message.error(x);
this.$toast.error(x);
}
}
);
},
deleteFlv(item) {
this.$Modal.confirm({
title: "提示",
content: "<p>是否删除Flv文件</p>",
onOk: () => {
window.ajax.get(
this.$confirm("是否删除Flv文件", "提示").then(result => {
if (result) {
return this.ajax.get(
"/record/flv/delete",
{ streamPath: item.Path.replace(".flv", "") },
x => {
if (x == "success") {
this.$Message.success("删除成功");
this.$toast.success("删除成功");
} else {
this.$Message.error(x);
this.$toast.error(x);
}
}
);
},
onCancel: () => {}
}
});
},
unitFormat: window.unitFormat,
toDurationStr(value) {
if (value > 1000) {
let s = value / 1000;
@@ -75,7 +74,7 @@ export default {
},
onVisible(visible) {
if (visible) {
window.ajax.getJSON("/record/flv/list", {}, x => {
this.ajax.getJSON("/record/flv/list", {}, x => {
this.data = x;
});
}