李勇

提交线上服务使用的版本

  1 +ALIBABA_CLOUD_ACCESS_KEY_ID='LTAI4G7fEeS2Mg9LuChgHpzg'
  2 +ALIBABA_CLOUD_ACCESS_KEY_SECRET='azh5WqDfS1wQo0TZgtJ0yr0vk827PW'
  3 +
@@ -5,6 +5,7 @@ var cookieParser = require('cookie-parser'); @@ -5,6 +5,7 @@ var cookieParser = require('cookie-parser');
5 var logger = require('morgan'); 5 var logger = require('morgan');
6 6
7 var indexRouter = require('./routes/index'); 7 var indexRouter = require('./routes/index');
  8 +var bbgRecordingRouter = require('./routes/bbgRecording');
8 9
9 var app = express(); 10 var app = express();
10 11
@@ -18,7 +19,18 @@ app.use(express.urlencoded({ extended: false })); @@ -18,7 +19,18 @@ app.use(express.urlencoded({ extended: false }));
18 app.use(cookieParser()); 19 app.use(cookieParser());
19 app.use(express.static(path.join(__dirname, 'public'))); 20 app.use(express.static(path.join(__dirname, 'public')));
20 21
  22 +app.all("*",function(req,res,next){
  23 + //设置允许跨域的域名,*代表允许任意域名跨域
  24 + res.header("Access-Control-Allow-Origin","*");
  25 + //允许的header类型
  26 + res.header("Access-Control-Allow-Headers","content-type");
  27 + //跨域允许的请求方式
  28 + res.header("Access-Control-Allow-Methods","POST,OPTIONS");
  29 + next();
  30 +});
  31 +
21 app.use('/', indexRouter); 32 app.use('/', indexRouter);
  33 +app.use('/mp4record',bbgRecordingRouter);
22 34
23 // catch 404 and forward to error handler 35 // catch 404 and forward to error handler
24 app.use(function(req, res, next) { 36 app.use(function(req, res, next) {
@@ -28,12 +40,13 @@ app.use(function(req, res, next) { @@ -28,12 +40,13 @@ app.use(function(req, res, next) {
28 // error handler 40 // error handler
29 app.use(function(err, req, res, next) { 41 app.use(function(err, req, res, next) {
30 // set locals, only providing error in development 42 // set locals, only providing error in development
31 - res.locals.message = err.message;  
32 - res.locals.error = req.app.get('env') === 'development' ? err : {}; 43 + //res.locals.message = err.message;
  44 + //res.locals.error = req.app.get('env') === 'development' ? err : {};
33 45
34 // render the error page 46 // render the error page
35 - res.status(err.status || 500);  
36 - res.render('error'); 47 + // res.status(err.status || 500);
  48 + //res.render('error');
  49 + res.send({ code: err.status || 500 });
37 }); 50 });
38 51
39 module.exports = app; 52 module.exports = app;
  1 +var createError = require('http-errors');
  2 +var express = require('express');
  3 +var path = require('path');
  4 +var cookieParser = require('cookie-parser');
  5 +var logger = require('morgan');
  6 +
  7 +var indexRouter = require('./routes/index');
  8 +
  9 +var app = express();
  10 +
  11 +// view engine setup
  12 +app.set('views', path.join(__dirname, 'views'));
  13 +app.set('view engine', 'jade');
  14 +
  15 +app.use(logger('dev'));
  16 +app.use(express.json());
  17 +app.use(express.urlencoded({ extended: false }));
  18 +app.use(cookieParser());
  19 +app.use(express.static(path.join(__dirname, 'public')));
  20 +
  21 +app.use('/', indexRouter);
  22 +
  23 +// catch 404 and forward to error handler
  24 +app.use(function(req, res, next) {
  25 + next(createError(404));
  26 +});
  27 +
  28 +// error handler
  29 +app.use(function(err, req, res, next) {
  30 + // set locals, only providing error in development
  31 + res.locals.message = err.message;
  32 + res.locals.error = req.app.get('env') === 'development' ? err : {};
  33 +
  34 + // render the error page
  35 + res.status(err.status || 500);
  36 + res.render('error');
  37 +});
  38 +
  39 +module.exports = app;
@@ -12,7 +12,8 @@ var http = require('http'); @@ -12,7 +12,8 @@ var http = require('http');
12 * Get port from environment and store in Express. 12 * Get port from environment and store in Express.
13 */ 13 */
14 14
15 -var port = normalizePort(process.env.PORT || '3001'); 15 +var port = normalizePort( '3001');
  16 +console.log("port",port)
16 app.set('port', port); 17 app.set('port', port);
17 18
18 /** 19 /**
1 { 1 {
2 - "GETCLASSURL":"http://139.196.126.156:8081/getLogs/recordClassList", 2 + "GETCLASSURL":"https://log3.xuedianyun.com/getLogs/recordClassList",
3 "GETCLASSURLPARAMETER":{ 3 "GETCLASSURLPARAMETER":{
4 - "siteId":["beidatest"], 4 + "siteId":["chindle","skyy","hopefound","beidatest"],
5 "key":"xdymp4record20191225", 5 "key":"xdymp4record20191225",
6 "page":1, 6 "page":1,
7 - "maxMedia":1 7 + "maxMedia":23
8 }, 8 },
9 - "PROJECTWINCATALOG":"F:/project/web_capture_release/win-x64",  
10 - "PROJECTCATALOG":"F:/project/web_capture_release", 9 + "PROJECTWINCATALOG":"/root/web_capture_release/linux-x64",
  10 + "PROJECTCATALOG":"/root/web_capture_release",
11 "BACKMEDIACONFIG":{ 11 "BACKMEDIACONFIG":{
12 "url" : "https://pclive.xuedianyun.com/pcBase/pclive2/dev/index.html", 12 "url" : "https://pclive.xuedianyun.com/pcBase/pclive2/dev/index.html",
13 "recordMp4":true, 13 "recordMp4":true,
@@ -25,5 +25,6 @@ @@ -25,5 +25,6 @@
25 "k":0, 25 "k":0,
26 "w":1280, 26 "w":1280,
27 "h":720 27 "h":720
28 - }  
29 -}  
  28 + },
  29 + "classLastNumber":["0","1","2","3","4","5","6","7","8","9"]
  30 +}
@@ -5,38 +5,49 @@ const methods = { @@ -5,38 +5,49 @@ const methods = {
5 let YesterdayTime = (new Date).getTime() - 24 * 60 * 60 * 1000 5 let YesterdayTime = (new Date).getTime() - 24 * 60 * 60 * 1000
6 let YesterdayTimeDate = new Date(YesterdayTime) 6 let YesterdayTimeDate = new Date(YesterdayTime)
7 let year = YesterdayTimeDate.getFullYear() 7 let year = YesterdayTimeDate.getFullYear()
  8 + year = year.toString()
8 let month = YesterdayTimeDate.getMonth() + 1 9 let month = YesterdayTimeDate.getMonth() + 1
9 let date = YesterdayTimeDate.getDate() 10 let date = YesterdayTimeDate.getDate()
10 let startTime = new Date(year + "-" + month + "-" + date + " 5:30:00").getTime() 11 let startTime = new Date(year + "-" + month + "-" + date + " 5:30:00").getTime()
11 let endTime = new Date(year + "-" + month + "-" + date + " 23:59:00").getTime() 12 let endTime = new Date(year + "-" + month + "-" + date + " 23:59:00").getTime()
12 - if(month < 10){ 13 + if (month < 10) {
13 month = '0' + month 14 month = '0' + month
  15 + }else{
  16 + month = month.toString()
14 } 17 }
15 - if(date < 10){ 18 +
  19 + if (date < 10) {
16 date = '0' + date 20 date = '0' + date
  21 + }else{
  22 + date = date.toString()
17 } 23 }
18 return { 24 return {
19 startTime, 25 startTime,
20 endTime, 26 endTime,
21 - ymd:year+month+date 27 + ymd: year + month + date
22 } 28 }
23 }, 29 },
24 dayTimeYMD() { 30 dayTimeYMD() {
25 let dayTimeDate = new Date() 31 let dayTimeDate = new Date()
26 let year = dayTimeDate.getFullYear() 32 let year = dayTimeDate.getFullYear()
  33 + year = year.toString()
27 let month = dayTimeDate.getMonth() + 1 34 let month = dayTimeDate.getMonth() + 1
28 let date = dayTimeDate.getDate() 35 let date = dayTimeDate.getDate()
29 - if(month < 10){ 36 + if (month < 10) {
30 month = '0' + month 37 month = '0' + month
  38 + }else{
  39 + month = month.toString()
31 } 40 }
32 - if(date < 10){ 41 + if (date < 10) {
33 date = '0' + date 42 date = '0' + date
  43 + }else{
  44 + date = date.toString()
34 } 45 }
35 return { 46 return {
36 - ymd:year + "-" + month + "-" + date 47 + ymd: year + month + date
37 } 48 }
38 }, 49 },
39 - async getRequestClassIds(url, siteId, key, startTime, endTime,page) { 50 + async getRequestClassIds(url, siteId, key, startTime, endTime, page) {
40 let axiosUrl = `${url}?siteId=${siteId}&key=${key}&from=${startTime}&to=${endTime}&page=${page}` 51 let axiosUrl = `${url}?siteId=${siteId}&key=${key}&from=${startTime}&to=${endTime}&page=${page}`
41 let result = await axios.get(axiosUrl) 52 let result = await axios.get(axiosUrl)
42 return result 53 return result
  1 +{
  2 + "PROJECTWINCATALOG":"/root/web_capture_release/linux-x64",
  3 + "PROJECTCATALOG":"/oss/oss",
  4 + "BACKMEDIACONFIG": {
  5 + "url": "https://pclive.xuedianyun.com/pcBase/pclive2/dev/index.html",
  6 + "recordMp4": true,
  7 + "classId": "",
  8 + "userId": 0,
  9 + "userName": "",
  10 + "userRole": "invisible",
  11 + "portalIP": "saas.xuedianyun.com",
  12 + "portalPort": 80,
  13 + "channels": 0,
  14 + "playRecord": 1,
  15 + "d": 21600000,
  16 + "s": 3000,
  17 + "fa": 15,
  18 + "k": 0,
  19 + "w": 1280,
  20 + "h": 720
  21 + }
  22 +}
@@ -26,6 +26,80 @@ @@ -26,6 +26,80 @@
26 "acorn": "^2.1.0" 26 "acorn": "^2.1.0"
27 } 27 }
28 }, 28 },
  29 + "address": {
  30 + "version": "1.2.2",
  31 + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz",
  32 + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA=="
  33 + },
  34 + "agentkeepalive": {
  35 + "version": "3.5.3",
  36 + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.3.tgz",
  37 + "integrity": "sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw==",
  38 + "requires": {
  39 + "humanize-ms": "^1.2.1"
  40 + }
  41 + },
  42 + "ali-oss": {
  43 + "version": "6.22.0",
  44 + "resolved": "https://registry.npmjs.org/ali-oss/-/ali-oss-6.22.0.tgz",
  45 + "integrity": "sha512-X8CHo+wsjCBvDaEvuibFOi3SZxiCBZSRUURrXH0upoVwu3SuW3e+PTVK7xw+uN6EyTcAESqrngrQimhp8iBzsQ==",
  46 + "requires": {
  47 + "address": "^1.2.2",
  48 + "agentkeepalive": "^3.4.1",
  49 + "bowser": "^1.6.0",
  50 + "copy-to": "^2.0.1",
  51 + "dateformat": "^2.0.0",
  52 + "debug": "^4.3.4",
  53 + "destroy": "^1.0.4",
  54 + "end-or-error": "^1.0.1",
  55 + "get-ready": "^1.0.0",
  56 + "humanize-ms": "^1.2.0",
  57 + "is-type-of": "^1.4.0",
  58 + "js-base64": "^2.5.2",
  59 + "jstoxml": "^2.0.0",
  60 + "lodash": "^4.17.21",
  61 + "merge-descriptors": "^1.0.1",
  62 + "mime": "^2.4.5",
  63 + "platform": "^1.3.1",
  64 + "pump": "^3.0.0",
  65 + "qs": "^6.4.0",
  66 + "sdk-base": "^2.0.1",
  67 + "stream-http": "2.8.2",
  68 + "stream-wormhole": "^1.0.4",
  69 + "urllib": "^2.44.0",
  70 + "utility": "^1.18.0",
  71 + "xml2js": "^0.6.2"
  72 + },
  73 + "dependencies": {
  74 + "debug": {
  75 + "version": "4.4.0",
  76 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
  77 + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
  78 + "requires": {
  79 + "ms": "^2.1.3"
  80 + }
  81 + },
  82 + "mime": {
  83 + "version": "2.6.0",
  84 + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
  85 + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
  86 + },
  87 + "ms": {
  88 + "version": "2.1.3",
  89 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
  90 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
  91 + },
  92 + "xml2js": {
  93 + "version": "0.6.2",
  94 + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
  95 + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
  96 + "requires": {
  97 + "sax": ">=0.6.0",
  98 + "xmlbuilder": "~11.0.0"
  99 + }
  100 + }
  101 + }
  102 + },
29 "align-text": { 103 "align-text": {
30 "version": "0.1.4", 104 "version": "0.1.4",
31 "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", 105 "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
@@ -41,6 +115,11 @@ @@ -41,6 +115,11 @@
41 "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", 115 "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
42 "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" 116 "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
43 }, 117 },
  118 + "any-promise": {
  119 + "version": "1.3.0",
  120 + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
  121 + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
  122 + },
44 "array-flatten": { 123 "array-flatten": {
45 "version": "1.1.1", 124 "version": "1.1.1",
46 "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 125 "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -97,6 +176,11 @@ @@ -97,6 +176,11 @@
97 "type-is": "~1.6.16" 176 "type-is": "~1.6.16"
98 } 177 }
99 }, 178 },
  179 + "bowser": {
  180 + "version": "1.9.4",
  181 + "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz",
  182 + "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ=="
  183 + },
100 "brace-expansion": { 184 "brace-expansion": {
101 "version": "1.1.11", 185 "version": "1.1.11",
102 "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 186 "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -106,6 +190,11 @@ @@ -106,6 +190,11 @@
106 "concat-map": "0.0.1" 190 "concat-map": "0.0.1"
107 } 191 }
108 }, 192 },
  193 + "builtin-status-codes": {
  194 + "version": "3.0.0",
  195 + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
  196 + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ=="
  197 + },
109 "bytes": { 198 "bytes": {
110 "version": "3.0.0", 199 "version": "3.0.0",
111 "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 200 "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@@ -218,6 +307,16 @@ @@ -218,6 +307,16 @@
218 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 307 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
219 "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 308 "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
220 }, 309 },
  310 + "copy-to": {
  311 + "version": "2.0.1",
  312 + "resolved": "https://registry.npmjs.org/copy-to/-/copy-to-2.0.1.tgz",
  313 + "integrity": "sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w=="
  314 + },
  315 + "core-util-is": {
  316 + "version": "1.0.3",
  317 + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
  318 + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
  319 + },
221 "css": { 320 "css": {
222 "version": "1.0.8", 321 "version": "1.0.8",
223 "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz", 322 "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz",
@@ -237,6 +336,16 @@ @@ -237,6 +336,16 @@
237 "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz", 336 "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz",
238 "integrity": "sha1-sNBClG2ylTu50pKQCmy19tASIDE=" 337 "integrity": "sha1-sNBClG2ylTu50pKQCmy19tASIDE="
239 }, 338 },
  339 + "date-format": {
  340 + "version": "3.0.0",
  341 + "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz",
  342 + "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w=="
  343 + },
  344 + "dateformat": {
  345 + "version": "2.2.0",
  346 + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz",
  347 + "integrity": "sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw=="
  348 + },
240 "debug": { 349 "debug": {
241 "version": "2.6.9", 350 "version": "2.6.9",
242 "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 351 "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -250,6 +359,14 @@ @@ -250,6 +359,14 @@
250 "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 359 "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
251 "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 360 "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
252 }, 361 },
  362 + "default-user-agent": {
  363 + "version": "1.0.0",
  364 + "resolved": "https://registry.npmjs.org/default-user-agent/-/default-user-agent-1.0.0.tgz",
  365 + "integrity": "sha512-bDF7bg6OSNcSwFWPu4zYKpVkJZQYVrAANMYB8bc9Szem1D0yKdm4sa/rOCs2aC9+2GMqQ7KnwtZRvDhmLF0dXw==",
  366 + "requires": {
  367 + "os-name": "~1.0.3"
  368 + }
  369 + },
253 "define-properties": { 370 "define-properties": {
254 "version": "1.1.3", 371 "version": "1.1.3",
255 "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 372 "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@@ -268,6 +385,16 @@ @@ -268,6 +385,16 @@
268 "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 385 "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
269 "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 386 "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
270 }, 387 },
  388 + "digest-header": {
  389 + "version": "1.1.0",
  390 + "resolved": "https://registry.npmjs.org/digest-header/-/digest-header-1.1.0.tgz",
  391 + "integrity": "sha512-glXVh42vz40yZb9Cq2oMOt70FIoWiv+vxNvdKdU8CwjLad25qHM3trLxhl9bVjdr6WaslIXhWpn0NO8T/67Qjg=="
  392 + },
  393 + "dotenv": {
  394 + "version": "16.4.7",
  395 + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
  396 + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="
  397 + },
271 "ee-first": { 398 "ee-first": {
272 "version": "1.1.1", 399 "version": "1.1.1",
273 "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 400 "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -278,6 +405,19 @@ @@ -278,6 +405,19 @@
278 "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 405 "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
279 "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 406 "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
280 }, 407 },
  408 + "end-of-stream": {
  409 + "version": "1.4.4",
  410 + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
  411 + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
  412 + "requires": {
  413 + "once": "^1.4.0"
  414 + }
  415 + },
  416 + "end-or-error": {
  417 + "version": "1.0.1",
  418 + "resolved": "https://registry.npmjs.org/end-or-error/-/end-or-error-1.0.1.tgz",
  419 + "integrity": "sha512-OclLMSug+k2A0JKuf494im25ANRBVW8qsjmwbgX7lQ8P82H21PQ1PWkoYwb9y5yMBS69BPlwtzdIFClo3+7kOQ=="
  420 + },
281 "es-abstract": { 421 "es-abstract": {
282 "version": "1.17.0", 422 "version": "1.17.0",
283 "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz", 423 "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz",
@@ -311,6 +451,15 @@ @@ -311,6 +451,15 @@
311 "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 451 "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
312 "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 452 "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
313 }, 453 },
  454 + "esdk-obs-nodejs": {
  455 + "version": "3.20.11",
  456 + "resolved": "https://registry.npmjs.org/esdk-obs-nodejs/-/esdk-obs-nodejs-3.20.11.tgz",
  457 + "integrity": "sha512-pFdlFIV24yLXYwIg6UZz/xFHom5bTF+D5yyJHxngoP4o6KfxVIid3BTMPLh6MVTpSLBgiTwiGKfvvAJavH240g==",
  458 + "requires": {
  459 + "log4js": "^6.3.0",
  460 + "xml2js": "^0.4.23"
  461 + }
  462 + },
314 "etag": { 463 "etag": {
315 "version": "1.8.1", 464 "version": "1.8.1",
316 "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 465 "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -353,6 +502,14 @@ @@ -353,6 +502,14 @@
353 "vary": "~1.1.2" 502 "vary": "~1.1.2"
354 } 503 }
355 }, 504 },
  505 + "extend-shallow": {
  506 + "version": "2.0.1",
  507 + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
  508 + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
  509 + "requires": {
  510 + "is-extendable": "^0.1.0"
  511 + }
  512 + },
356 "finalhandler": { 513 "finalhandler": {
357 "version": "1.1.1", 514 "version": "1.1.1",
358 "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 515 "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
@@ -367,6 +524,11 @@ @@ -367,6 +524,11 @@
367 "unpipe": "~1.0.0" 524 "unpipe": "~1.0.0"
368 } 525 }
369 }, 526 },
  527 + "flatted": {
  528 + "version": "2.0.2",
  529 + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
  530 + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA=="
  531 + },
370 "follow-redirects": { 532 "follow-redirects": {
371 "version": "1.5.10", 533 "version": "1.5.10",
372 "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", 534 "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
@@ -385,6 +547,24 @@ @@ -385,6 +547,24 @@
385 } 547 }
386 } 548 }
387 }, 549 },
  550 + "formstream": {
  551 + "version": "1.5.1",
  552 + "resolved": "https://registry.npmjs.org/formstream/-/formstream-1.5.1.tgz",
  553 + "integrity": "sha512-q7ORzFqotpwn3Y/GBK2lK7PjtZZwJHz9QE9Phv8zb5IrL9ftGLyi2zjGURON3voK8TaZ+mqJKERYN4lrHYTkUQ==",
  554 + "requires": {
  555 + "destroy": "^1.0.4",
  556 + "mime": "^2.5.2",
  557 + "node-hex": "^1.0.1",
  558 + "pause-stream": "~0.0.11"
  559 + },
  560 + "dependencies": {
  561 + "mime": {
  562 + "version": "2.6.0",
  563 + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
  564 + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
  565 + }
  566 + }
  567 + },
388 "forwarded": { 568 "forwarded": {
389 "version": "0.1.2", 569 "version": "0.1.2",
390 "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 570 "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -395,6 +575,16 @@ @@ -395,6 +575,16 @@
395 "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 575 "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
396 "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 576 "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
397 }, 577 },
  578 + "fs-extra": {
  579 + "version": "8.1.0",
  580 + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
  581 + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
  582 + "requires": {
  583 + "graceful-fs": "^4.2.0",
  584 + "jsonfile": "^4.0.0",
  585 + "universalify": "^0.1.0"
  586 + }
  587 + },
398 "fs.realpath": { 588 "fs.realpath": {
399 "version": "1.0.0", 589 "version": "1.0.0",
400 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 590 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -405,6 +595,11 @@ @@ -405,6 +595,11 @@
405 "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 595 "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
406 "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 596 "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
407 }, 597 },
  598 + "get-ready": {
  599 + "version": "1.0.0",
  600 + "resolved": "https://registry.npmjs.org/get-ready/-/get-ready-1.0.0.tgz",
  601 + "integrity": "sha512-mFXCZPJIlcYcth+N8267+mghfYN9h3EhsDa6JSnbA3Wrhh/XFpuowviFcsDeYZtKspQyWyJqfs4O6P8CHeTwzw=="
  602 + },
408 "glob": { 603 "glob": {
409 "version": "7.1.6", 604 "version": "7.1.6",
410 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 605 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -418,6 +613,11 @@ @@ -418,6 +613,11 @@
418 "path-is-absolute": "^1.0.0" 613 "path-is-absolute": "^1.0.0"
419 } 614 }
420 }, 615 },
  616 + "graceful-fs": {
  617 + "version": "4.2.4",
  618 + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
  619 + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
  620 + },
421 "graceful-readlink": { 621 "graceful-readlink": {
422 "version": "1.0.1", 622 "version": "1.0.1",
423 "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 623 "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
@@ -447,6 +647,14 @@ @@ -447,6 +647,14 @@
447 "statuses": ">= 1.4.0 < 2" 647 "statuses": ">= 1.4.0 < 2"
448 } 648 }
449 }, 649 },
  650 + "humanize-ms": {
  651 + "version": "1.2.1",
  652 + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
  653 + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
  654 + "requires": {
  655 + "ms": "^2.0.0"
  656 + }
  657 + },
450 "iconv-lite": { 658 "iconv-lite": {
451 "version": "0.4.23", 659 "version": "0.4.23",
452 "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 660 "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
@@ -494,11 +702,21 @@ @@ -494,11 +702,21 @@
494 "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", 702 "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
495 "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" 703 "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
496 }, 704 },
  705 + "is-class-hotfix": {
  706 + "version": "0.0.6",
  707 + "resolved": "https://registry.npmjs.org/is-class-hotfix/-/is-class-hotfix-0.0.6.tgz",
  708 + "integrity": "sha512-0n+pzCC6ICtVr/WXnN2f03TK/3BfXY7me4cjCAqT8TYXEl0+JBRoqBo94JJHXcyDSLUeWbNX8Fvy5g5RJdAstQ=="
  709 + },
497 "is-date-object": { 710 "is-date-object": {
498 "version": "1.0.2", 711 "version": "1.0.2",
499 "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", 712 "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
500 "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" 713 "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
501 }, 714 },
  715 + "is-extendable": {
  716 + "version": "0.1.1",
  717 + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
  718 + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
  719 + },
502 "is-generator-function": { 720 "is-generator-function": {
503 "version": "1.0.7", 721 "version": "1.0.7",
504 "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", 722 "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz",
@@ -525,6 +743,26 @@ @@ -525,6 +743,26 @@
525 "has-symbols": "^1.0.1" 743 "has-symbols": "^1.0.1"
526 } 744 }
527 }, 745 },
  746 + "is-type-of": {
  747 + "version": "1.4.0",
  748 + "resolved": "https://registry.npmjs.org/is-type-of/-/is-type-of-1.4.0.tgz",
  749 + "integrity": "sha512-EddYllaovi5ysMLMEN7yzHEKh8A850cZ7pykrY1aNRQGn/CDjRDE9qEWbIdt7xGEVJmjBXzU/fNnC4ABTm8tEQ==",
  750 + "requires": {
  751 + "core-util-is": "^1.0.2",
  752 + "is-class-hotfix": "~0.0.6",
  753 + "isstream": "~0.1.2"
  754 + }
  755 + },
  756 + "isarray": {
  757 + "version": "1.0.0",
  758 + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
  759 + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
  760 + },
  761 + "isstream": {
  762 + "version": "0.1.2",
  763 + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
  764 + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
  765 + },
528 "jade": { 766 "jade": {
529 "version": "1.11.0", 767 "version": "1.11.0",
530 "resolved": "https://registry.npmjs.org/jade/-/jade-1.11.0.tgz", 768 "resolved": "https://registry.npmjs.org/jade/-/jade-1.11.0.tgz",
@@ -542,6 +780,24 @@ @@ -542,6 +780,24 @@
542 "with": "~4.0.0" 780 "with": "~4.0.0"
543 } 781 }
544 }, 782 },
  783 + "js-base64": {
  784 + "version": "2.6.4",
  785 + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
  786 + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ=="
  787 + },
  788 + "jsonfile": {
  789 + "version": "4.0.0",
  790 + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
  791 + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
  792 + "requires": {
  793 + "graceful-fs": "^4.1.6"
  794 + }
  795 + },
  796 + "jstoxml": {
  797 + "version": "2.2.9",
  798 + "resolved": "https://registry.npmjs.org/jstoxml/-/jstoxml-2.2.9.tgz",
  799 + "integrity": "sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw=="
  800 + },
545 "jstransformer": { 801 "jstransformer": {
546 "version": "0.0.2", 802 "version": "0.0.2",
547 "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz", 803 "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz",
@@ -564,6 +820,38 @@ @@ -564,6 +820,38 @@
564 "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 820 "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
565 "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" 821 "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
566 }, 822 },
  823 + "lodash": {
  824 + "version": "4.17.21",
  825 + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
  826 + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
  827 + },
  828 + "log4js": {
  829 + "version": "6.3.0",
  830 + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz",
  831 + "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==",
  832 + "requires": {
  833 + "date-format": "^3.0.0",
  834 + "debug": "^4.1.1",
  835 + "flatted": "^2.0.1",
  836 + "rfdc": "^1.1.4",
  837 + "streamroller": "^2.2.4"
  838 + },
  839 + "dependencies": {
  840 + "debug": {
  841 + "version": "4.3.1",
  842 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
  843 + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
  844 + "requires": {
  845 + "ms": "2.1.2"
  846 + }
  847 + },
  848 + "ms": {
  849 + "version": "2.1.2",
  850 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
  851 + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
  852 + }
  853 + }
  854 + },
567 "longest": { 855 "longest": {
568 "version": "1.0.1", 856 "version": "1.0.1",
569 "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 857 "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
@@ -640,11 +928,31 @@ @@ -640,11 +928,31 @@
640 "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 928 "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
641 "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 929 "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
642 }, 930 },
  931 + "mz": {
  932 + "version": "2.7.0",
  933 + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
  934 + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
  935 + "requires": {
  936 + "any-promise": "^1.0.0",
  937 + "object-assign": "^4.0.1",
  938 + "thenify-all": "^1.0.0"
  939 + }
  940 + },
643 "negotiator": { 941 "negotiator": {
644 "version": "0.6.2", 942 "version": "0.6.2",
645 "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 943 "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
646 "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 944 "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
647 }, 945 },
  946 + "node-hex": {
  947 + "version": "1.0.1",
  948 + "resolved": "https://registry.npmjs.org/node-hex/-/node-hex-1.0.1.tgz",
  949 + "integrity": "sha512-iwpZdvW6Umz12ICmu9IYPRxg0tOLGmU3Tq2tKetejCj3oZd7b2nUXwP3a7QA5M9glWy8wlPS1G3RwM/CdsUbdQ=="
  950 + },
  951 + "object-assign": {
  952 + "version": "4.1.1",
  953 + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
  954 + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
  955 + },
648 "object-inspect": { 956 "object-inspect": {
649 "version": "1.7.0", 957 "version": "1.7.0",
650 "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", 958 "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
@@ -706,6 +1014,30 @@ @@ -706,6 +1014,30 @@
706 "wordwrap": "~0.0.2" 1014 "wordwrap": "~0.0.2"
707 } 1015 }
708 }, 1016 },
  1017 + "os-name": {
  1018 + "version": "1.0.3",
  1019 + "resolved": "https://registry.npmjs.org/os-name/-/os-name-1.0.3.tgz",
  1020 + "integrity": "sha512-f5estLO2KN8vgtTRaILIgEGBoBrMnZ3JQ7W9TMZCnOIGwHe8TRGSpcagnWDo+Dfhd/z08k9Xe75hvciJJ8Qaew==",
  1021 + "requires": {
  1022 + "osx-release": "^1.0.0",
  1023 + "win-release": "^1.0.0"
  1024 + }
  1025 + },
  1026 + "osx-release": {
  1027 + "version": "1.1.0",
  1028 + "resolved": "https://registry.npmjs.org/osx-release/-/osx-release-1.1.0.tgz",
  1029 + "integrity": "sha512-ixCMMwnVxyHFQLQnINhmIpWqXIfS2YOXchwQrk+OFzmo6nDjQ0E4KXAyyUh0T0MZgV4bUhkRrAbVqlE4yLVq4A==",
  1030 + "requires": {
  1031 + "minimist": "^1.1.0"
  1032 + },
  1033 + "dependencies": {
  1034 + "minimist": {
  1035 + "version": "1.2.8",
  1036 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
  1037 + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
  1038 + }
  1039 + }
  1040 + },
709 "parseurl": { 1041 "parseurl": {
710 "version": "1.3.3", 1042 "version": "1.3.3",
711 "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1043 "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -726,6 +1058,24 @@ @@ -726,6 +1058,24 @@
726 "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1058 "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
727 "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 1059 "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
728 }, 1060 },
  1061 + "pause-stream": {
  1062 + "version": "0.0.11",
  1063 + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
  1064 + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==",
  1065 + "requires": {
  1066 + "through": "~2.3"
  1067 + }
  1068 + },
  1069 + "platform": {
  1070 + "version": "1.3.6",
  1071 + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
  1072 + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="
  1073 + },
  1074 + "process-nextick-args": {
  1075 + "version": "2.0.1",
  1076 + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
  1077 + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
  1078 + },
729 "promise": { 1079 "promise": {
730 "version": "6.1.0", 1080 "version": "6.1.0",
731 "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", 1081 "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz",
@@ -743,6 +1093,15 @@ @@ -743,6 +1093,15 @@
743 "ipaddr.js": "1.9.0" 1093 "ipaddr.js": "1.9.0"
744 } 1094 }
745 }, 1095 },
  1096 + "pump": {
  1097 + "version": "3.0.2",
  1098 + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
  1099 + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
  1100 + "requires": {
  1101 + "end-of-stream": "^1.1.0",
  1102 + "once": "^1.3.1"
  1103 + }
  1104 + },
746 "qs": { 1105 "qs": {
747 "version": "6.5.2", 1106 "version": "6.5.2",
748 "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1107 "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
@@ -764,6 +1123,20 @@ @@ -764,6 +1123,20 @@
764 "unpipe": "1.0.0" 1123 "unpipe": "1.0.0"
765 } 1124 }
766 }, 1125 },
  1126 + "readable-stream": {
  1127 + "version": "2.3.8",
  1128 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
  1129 + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
  1130 + "requires": {
  1131 + "core-util-is": "~1.0.0",
  1132 + "inherits": "~2.0.3",
  1133 + "isarray": "~1.0.0",
  1134 + "process-nextick-args": "~2.0.0",
  1135 + "safe-buffer": "~5.1.1",
  1136 + "string_decoder": "~1.1.1",
  1137 + "util-deprecate": "~1.0.1"
  1138 + }
  1139 + },
767 "rechoir": { 1140 "rechoir": {
768 "version": "0.6.2", 1141 "version": "0.6.2",
769 "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", 1142 "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
@@ -785,6 +1158,11 @@ @@ -785,6 +1158,11 @@
785 "path-parse": "^1.0.6" 1158 "path-parse": "^1.0.6"
786 } 1159 }
787 }, 1160 },
  1161 + "rfdc": {
  1162 + "version": "1.2.0",
  1163 + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.2.0.tgz",
  1164 + "integrity": "sha512-ijLyszTMmUrXvjSooucVQwimGUk84eRcmCuLV8Xghe3UO85mjUtRAHRyoMM6XtyqbECaXuBWx18La3523sXINA=="
  1165 + },
788 "right-align": { 1166 "right-align": {
789 "version": "0.1.3", 1167 "version": "0.1.3",
790 "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 1168 "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
@@ -803,6 +1181,24 @@ @@ -803,6 +1181,24 @@
803 "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1181 "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
804 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1182 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
805 }, 1183 },
  1184 + "sax": {
  1185 + "version": "1.2.4",
  1186 + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
  1187 + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
  1188 + },
  1189 + "sdk-base": {
  1190 + "version": "2.0.1",
  1191 + "resolved": "https://registry.npmjs.org/sdk-base/-/sdk-base-2.0.1.tgz",
  1192 + "integrity": "sha512-eeG26wRwhtwYuKGCDM3LixCaxY27Pa/5lK4rLKhQa7HBjJ3U3Y+f81MMZQRsDw/8SC2Dao/83yJTXJ8aULuN8Q==",
  1193 + "requires": {
  1194 + "get-ready": "~1.0.0"
  1195 + }
  1196 + },
  1197 + "semver": {
  1198 + "version": "5.7.2",
  1199 + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
  1200 + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
  1201 + },
806 "send": { 1202 "send": {
807 "version": "0.16.2", 1203 "version": "0.16.2",
808 "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 1204 "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
@@ -862,6 +1258,53 @@ @@ -862,6 +1258,53 @@
862 "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 1258 "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
863 "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 1259 "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
864 }, 1260 },
  1261 + "stream-http": {
  1262 + "version": "2.8.2",
  1263 + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.2.tgz",
  1264 + "integrity": "sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==",
  1265 + "requires": {
  1266 + "builtin-status-codes": "^3.0.0",
  1267 + "inherits": "^2.0.1",
  1268 + "readable-stream": "^2.3.6",
  1269 + "to-arraybuffer": "^1.0.0",
  1270 + "xtend": "^4.0.0"
  1271 + }
  1272 + },
  1273 + "stream-wormhole": {
  1274 + "version": "1.1.0",
  1275 + "resolved": "https://registry.npmjs.org/stream-wormhole/-/stream-wormhole-1.1.0.tgz",
  1276 + "integrity": "sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew=="
  1277 + },
  1278 + "streamroller": {
  1279 + "version": "2.2.4",
  1280 + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz",
  1281 + "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==",
  1282 + "requires": {
  1283 + "date-format": "^2.1.0",
  1284 + "debug": "^4.1.1",
  1285 + "fs-extra": "^8.1.0"
  1286 + },
  1287 + "dependencies": {
  1288 + "date-format": {
  1289 + "version": "2.1.0",
  1290 + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
  1291 + "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA=="
  1292 + },
  1293 + "debug": {
  1294 + "version": "4.3.1",
  1295 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
  1296 + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
  1297 + "requires": {
  1298 + "ms": "2.1.2"
  1299 + }
  1300 + },
  1301 + "ms": {
  1302 + "version": "2.1.2",
  1303 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
  1304 + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
  1305 + }
  1306 + }
  1307 + },
865 "string.prototype.trimleft": { 1308 "string.prototype.trimleft": {
866 "version": "2.1.1", 1309 "version": "2.1.1",
867 "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", 1310 "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
@@ -880,6 +1323,40 @@ @@ -880,6 +1323,40 @@
880 "function-bind": "^1.1.1" 1323 "function-bind": "^1.1.1"
881 } 1324 }
882 }, 1325 },
  1326 + "string_decoder": {
  1327 + "version": "1.1.1",
  1328 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
  1329 + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
  1330 + "requires": {
  1331 + "safe-buffer": "~5.1.0"
  1332 + }
  1333 + },
  1334 + "thenify": {
  1335 + "version": "3.3.1",
  1336 + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
  1337 + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
  1338 + "requires": {
  1339 + "any-promise": "^1.0.0"
  1340 + }
  1341 + },
  1342 + "thenify-all": {
  1343 + "version": "1.6.0",
  1344 + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
  1345 + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
  1346 + "requires": {
  1347 + "thenify": ">= 3.1.0 < 4"
  1348 + }
  1349 + },
  1350 + "through": {
  1351 + "version": "2.3.8",
  1352 + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
  1353 + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
  1354 + },
  1355 + "to-arraybuffer": {
  1356 + "version": "1.0.1",
  1357 + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
  1358 + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA=="
  1359 + },
883 "transformers": { 1360 "transformers": {
884 "version": "2.1.0", 1361 "version": "2.1.0",
885 "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", 1362 "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz",
@@ -954,11 +1431,53 @@ @@ -954,11 +1431,53 @@
954 "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 1431 "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
955 "optional": true 1432 "optional": true
956 }, 1433 },
  1434 + "unescape": {
  1435 + "version": "1.0.1",
  1436 + "resolved": "https://registry.npmjs.org/unescape/-/unescape-1.0.1.tgz",
  1437 + "integrity": "sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==",
  1438 + "requires": {
  1439 + "extend-shallow": "^2.0.1"
  1440 + }
  1441 + },
  1442 + "universalify": {
  1443 + "version": "0.1.2",
  1444 + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
  1445 + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
  1446 + },
957 "unpipe": { 1447 "unpipe": {
958 "version": "1.0.0", 1448 "version": "1.0.0",
959 "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1449 "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
960 "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 1450 "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
961 }, 1451 },
  1452 + "urllib": {
  1453 + "version": "2.44.0",
  1454 + "resolved": "https://registry.npmjs.org/urllib/-/urllib-2.44.0.tgz",
  1455 + "integrity": "sha512-zRCJqdfYllRDA9bXUtx+vccyRqtJPKsw85f44zH7zPD28PIvjMqIgw9VwoTLV7xTBWZsbebUFVHU5ghQcWku2A==",
  1456 + "requires": {
  1457 + "any-promise": "^1.3.0",
  1458 + "content-type": "^1.0.2",
  1459 + "default-user-agent": "^1.0.0",
  1460 + "digest-header": "^1.0.0",
  1461 + "ee-first": "~1.1.1",
  1462 + "formstream": "^1.1.0",
  1463 + "humanize-ms": "^1.2.0",
  1464 + "iconv-lite": "^0.6.3",
  1465 + "pump": "^3.0.0",
  1466 + "qs": "^6.4.0",
  1467 + "statuses": "^1.3.1",
  1468 + "utility": "^1.16.1"
  1469 + },
  1470 + "dependencies": {
  1471 + "iconv-lite": {
  1472 + "version": "0.6.3",
  1473 + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
  1474 + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
  1475 + "requires": {
  1476 + "safer-buffer": ">= 2.1.2 < 3.0.0"
  1477 + }
  1478 + }
  1479 + }
  1480 + },
962 "util": { 1481 "util": {
963 "version": "0.12.1", 1482 "version": "0.12.1",
964 "resolved": "https://registry.npmjs.org/util/-/util-0.12.1.tgz", 1483 "resolved": "https://registry.npmjs.org/util/-/util-0.12.1.tgz",
@@ -971,6 +1490,23 @@ @@ -971,6 +1490,23 @@
971 "safe-buffer": "^5.1.2" 1490 "safe-buffer": "^5.1.2"
972 } 1491 }
973 }, 1492 },
  1493 + "util-deprecate": {
  1494 + "version": "1.0.2",
  1495 + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
  1496 + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
  1497 + },
  1498 + "utility": {
  1499 + "version": "1.18.0",
  1500 + "resolved": "https://registry.npmjs.org/utility/-/utility-1.18.0.tgz",
  1501 + "integrity": "sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==",
  1502 + "requires": {
  1503 + "copy-to": "^2.0.1",
  1504 + "escape-html": "^1.0.3",
  1505 + "mkdirp": "^0.5.1",
  1506 + "mz": "^2.7.0",
  1507 + "unescape": "^1.0.1"
  1508 + }
  1509 + },
974 "utils-merge": { 1510 "utils-merge": {
975 "version": "1.0.1", 1511 "version": "1.0.1",
976 "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1512 "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -986,6 +1522,14 @@ @@ -986,6 +1522,14 @@
986 "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", 1522 "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
987 "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" 1523 "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
988 }, 1524 },
  1525 + "win-release": {
  1526 + "version": "1.1.1",
  1527 + "resolved": "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz",
  1528 + "integrity": "sha512-iCRnKVvGxOQdsKhcQId2PXV1vV3J/sDPXKA4Oe9+Eti2nb2ESEsYHRYls/UjoUW3bIc5ZDO8dTH50A/5iVN+bw==",
  1529 + "requires": {
  1530 + "semver": "^5.0.1"
  1531 + }
  1532 + },
989 "window-size": { 1533 "window-size": {
990 "version": "0.1.0", 1534 "version": "0.1.0",
991 "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 1535 "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
@@ -1017,6 +1561,25 @@ @@ -1017,6 +1561,25 @@
1017 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1561 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1018 "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1562 "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
1019 }, 1563 },
  1564 + "xml2js": {
  1565 + "version": "0.4.23",
  1566 + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
  1567 + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
  1568 + "requires": {
  1569 + "sax": ">=0.6.0",
  1570 + "xmlbuilder": "~11.0.0"
  1571 + }
  1572 + },
  1573 + "xmlbuilder": {
  1574 + "version": "11.0.1",
  1575 + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
  1576 + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
  1577 + },
  1578 + "xtend": {
  1579 + "version": "4.0.2",
  1580 + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
  1581 + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
  1582 + },
1020 "yargs": { 1583 "yargs": {
1021 "version": "3.10.0", 1584 "version": "3.10.0",
1022 "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 1585 "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
@@ -3,14 +3,17 @@ @@ -3,14 +3,17 @@
3 "version": "0.0.0", 3 "version": "0.0.0",
4 "private": true, 4 "private": true,
5 "scripts": { 5 "scripts": {
6 - "start": "nodemon ./bin/www", 6 + "start": "node ./bin/www",
7 "pm2": "pm2 start ./bin/www --name webScreen" 7 "pm2": "pm2 start ./bin/www --name webScreen"
8 }, 8 },
9 "dependencies": { 9 "dependencies": {
  10 + "ali-oss": "^6.22.0",
10 "axios": "^0.19.0", 11 "axios": "^0.19.0",
11 "child_process": "^1.0.2", 12 "child_process": "^1.0.2",
12 "cookie-parser": "~1.4.4", 13 "cookie-parser": "~1.4.4",
13 "debug": "~2.6.9", 14 "debug": "~2.6.9",
  15 + "dotenv": "^16.4.7",
  16 + "esdk-obs-nodejs": "^3.20.11",
14 "express": "~4.16.1", 17 "express": "~4.16.1",
15 "http-errors": "~1.6.3", 18 "http-errors": "~1.6.3",
16 "jade": "~1.11.0", 19 "jade": "~1.11.0",
  1 +var express = require('express');
  2 +var router = express.Router();
  3 +const { exec } = require('child_process');
  4 +const fs = require("fs");
  5 +const axios = require('axios');
  6 +
  7 +const method = require("../config/method")
  8 +
  9 +const { dayTimeYMD } = method
  10 +
  11 +
  12 +let classids = []
  13 +
  14 +/**
  15 + *
  16 + * @param {*} id 课堂id
  17 + */
  18 +class MediaCreat {
  19 + constructor() {
  20 + }
  21 +
  22 + wrieLog(text) {
  23 + // 写入log
  24 + let logFile = `./log/${dayTimeYMD().ymd}.txt`
  25 + fs.appendFileSync(logFile, new Date().toLocaleString() + " " + text + '\r\n');
  26 + }
  27 +
  28 + recordingCreat(id,siteId,classUrl,duration,width,height,frameRate,bitrate) {
  29 + this.wrieLog(" 课堂录制开始:------>" + id)
  30 + let fileConfig = this.getConfigFileJson()
  31 + if (!fileConfig) return false
  32 + const { BACKMEDIACONFIG, PROJECTWINCATALOG, PROJECTCATALOG } = JSON.parse(fileConfig)
  33 + // let mediaDir = PROJECTCATALOG + "/media/"
  34 + // let classDir = PROJECTCATALOG + "/media/" + siteId
  35 + // let ymdDir = null
  36 + // let timeDir = dayTimeYMD().ymd
  37 + // ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + timeDir
  38 +
  39 +
  40 + let classDir = PROJECTCATALOG + "/" + siteId
  41 + let ymdDir = null
  42 + let timeDir = dayTimeYMD().ymd
  43 + ymdDir = PROJECTCATALOG + "/" + siteId + "/" + timeDir
  44 +
  45 +
  46 + if (!fs.existsSync(classDir)) {
  47 + fs.mkdirSync(classDir);
  48 + }
  49 + if (!fs.existsSync(ymdDir)) {
  50 + fs.mkdirSync(ymdDir);
  51 + }
  52 + // let files = fs.readdirSync(ymdDir);
  53 + // let interValGetFile = setInterval(()=>{
  54 + // this.wrieLog("files:" + files)
  55 + // if (files.indexOf(id + ".mp4") != -1) {
  56 + // this.wrieLog("已存在:" + id + "课堂号,停止继续录制")
  57 + // return
  58 + // }
  59 +
  60 + // if(!classUrl){
  61 + // classUrl = `${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}"`
  62 + // }
  63 + // if(!duration){
  64 + // duration = BACKMEDIACONFIG.d
  65 + // }
  66 + let url = `${PROJECTWINCATALOG}/web_capture_c -o=${ymdDir}/${id}.mp4 -u="${classUrl}" -d=${duration} -s=${BACKMEDIACONFIG.s} -k=${BACKMEDIACONFIG.k} -w=${width} -h=${height} -fa=${frameRate} -vb=${bitrate}`
  67 + this.wrieLog("全地址"+url)
  68 + exec(url, { maxBuffer: 1073741824 }, (err, stdout, stderr) => {
  69 + if (err != null) {
  70 + this.wrieLog(" 错误" + id + ":" + err)
  71 + this.wrieLog(" 错误 stdout" + id + ":" + stdout)
  72 + this.wrieLog(" 错误 stderr" + id + ":" + stderr)
  73 + // 删除已存元素
  74 + classids.splice(classids.findIndex(item => item === id),1)
  75 + return
  76 + }
  77 + // let files = fs.readdirSync(ymdDir);
  78 + // if (files.indexOf(id + ".mp4") != -1) {
  79 + // 删除已存元素
  80 + classids.splice(classids.findIndex(item => item === id),1)
  81 + this.sendMediaInfo(siteId,id,timeDir)
  82 + // }
  83 + })
  84 + }
  85 + // 上报地址
  86 + async sendMediaInfo(siteId,id,timeDir){
  87 + // https://mp4record.obs.cn-north-4.myhuaweicloud.com/oss/media/eebbktest/20210326/1657547103.mp4
  88 + // let MediaUrl = `https://mp4record.obs.cn-north-4.myhuaweicloud.com/oss/media/${siteId}/${timeDir}/${id}.mp4`
  89 + let MediaUrl = `https://mp4record.obs.cn-north-4.myhuaweicloud.com/oss/${siteId}/${timeDir}/${id}.mp4`
  90 + let urlSizeId = {
  91 + "eebbktest": "gdbbkwxyace",
  92 + "gdbbk":"gdbbkwx",
  93 + "gdbbkdev":"gdbbkwxtest"
  94 + }
  95 + let requestUrl = `https://${urlSizeId[siteId]}.xuedianyun.com/bbgserver/app/course/updateRecordUrl`
  96 + let result = await axios.post(
  97 + requestUrl,
  98 + {meetingNumber:id,url:MediaUrl},
  99 + {headers: {'orgId':siteId }}
  100 + )
  101 + if(result.data){
  102 + this.wrieLog("更新视频地址结果:"+JSON.stringify(result.data))
  103 + }
  104 + }
  105 +
  106 + getConfigFileJson() {
  107 + const buffer = fs.readFileSync(process.cwd() + "/config/realTimeConfig.json")
  108 + return String(buffer)
  109 + }
  110 +}
  111 +
  112 +/**
  113 + * {
  114 + * "classId":[{ classId: '389675110', siteId: 'kuaikuenglish' }]
  115 + * }
  116 + */
  117 +router.post('/recording/:id', function (req, res, next) {
  118 + new MediaCreat().wrieLog("录制启动:------>")
  119 + let fileConfig = new MediaCreat().getConfigFileJson()
  120 + if (!fileConfig) return false
  121 + /**
  122 + * classId:课堂号
  123 + * siteId:机构编码
  124 + * url:课堂地址
  125 + * duration:录制时长
  126 + * width height 录制视频宽高
  127 + * frameRate 帧率
  128 + * bitrate 码率
  129 + */
  130 + let { classId,siteId,url:classUrl,duration,width,height,frameRate,bitrate } = req.body
  131 +
  132 + if (classId && siteId && classUrl && duration && width && height && frameRate && bitrate) {
  133 + if(!Number.isInteger(classId) || !Number.isInteger(duration) || !Number.isInteger(width) || !Number.isInteger(height) || !Number.isInteger(frameRate) || !Number.isInteger(bitrate)){
  134 + res.send({ code: "3", message: "参数类型错误" });
  135 + return
  136 + }
  137 + let maxTime = 10*60*60*1000
  138 + if(duration < 0 || duration > maxTime){
  139 + res.send({ code: "4", message: "参数范围错误" });
  140 + return
  141 + }
  142 + // 避免多次重复课堂号请求
  143 + if(!classids.includes(classId)){
  144 + // 没有正在录制的该课堂
  145 + classids.push(classId)
  146 + bitrate = bitrate * 1024 // 转换 * 1024
  147 + new MediaCreat().recordingCreat(classId, siteId,classUrl,duration,width,height,frameRate,bitrate)
  148 + res.send({ code: "0" });
  149 + }else{
  150 + res.send({ code: "2", message: "重复请求课堂号" });
  151 + }
  152 + } else {
  153 + res.send({ code: "1", message: "缺少参数" });
  154 + }
  155 +
  156 +})
  157 +
  158 +
  159 +module.exports = router;
  1 +var express = require('express');
  2 +var router = express.Router();
  3 +const { exec } = require('child_process');
  4 +const fs = require("fs");
  5 +const axios = require('axios');
  6 +const method = require("../config/method")
  7 +
  8 +const { dayTimeYMD } = method
  9 +
  10 +
  11 +let classids = []
  12 +
  13 +/**
  14 + *
  15 + * @param {*} id 课堂id
  16 + */
  17 +class MediaCreat {
  18 + constructor() {
  19 + }
  20 +
  21 + wrieLog(text) {
  22 + // 写入log
  23 + let logFile = `./log/${dayTimeYMD().ymd}.txt`
  24 + fs.appendFileSync(logFile, new Date().toLocaleString() + " " + text + '\r\n');
  25 + }
  26 +
  27 + recordingCreat(id,siteId,classUrl,duration,width,height,frameRate,bitrate) {
  28 + this.wrieLog(" 课堂录制开始:------>" + id)
  29 + let fileConfig = this.getConfigFileJson()
  30 + if (!fileConfig) return false
  31 + const { BACKMEDIACONFIG, PROJECTWINCATALOG, PROJECTCATALOG } = JSON.parse(fileConfig)
  32 + let mediaDir = PROJECTCATALOG + "/media/"
  33 + let classDir = PROJECTCATALOG + "/media/" + siteId
  34 + let ymdDir = null
  35 + let timeDir = dayTimeYMD().ymd
  36 + ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + timeDir
  37 + if (!fs.existsSync(mediaDir)) {
  38 + fs.mkdirSync(mediaDir);
  39 + }
  40 + if (!fs.existsSync(classDir)) {
  41 + fs.mkdirSync(classDir);
  42 + }
  43 + if (!fs.existsSync(ymdDir)) {
  44 + fs.mkdirSync(ymdDir);
  45 + }
  46 + let files = fs.readdirSync(ymdDir);
  47 + // let interValGetFile = setInterval(()=>{
  48 + this.wrieLog("files:" + files)
  49 + if (files.indexOf(id + ".mp4") != -1) {
  50 + this.wrieLog("已存在:" + id + "课堂号,停止继续录制")
  51 + return
  52 + }
  53 +
  54 + // if(!classUrl){
  55 + // classUrl = `${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}"`
  56 + // }
  57 + // if(!duration){
  58 + // duration = BACKMEDIACONFIG.d
  59 + // }
  60 + let url = `${PROJECTWINCATALOG}/web_capture_c -o=${ymdDir}/${id}.mp4 -u="${classUrl}" -d=${duration} -s=${BACKMEDIACONFIG.s} -k=${BACKMEDIACONFIG.k} -w=${width} -h=${height} -fa=${frameRate} -vb=${bitrate}`
  61 + this.wrieLog("测试全地址"+url)
  62 + exec(url, { maxBuffer: 1073741824 }, (err, stdout, stderr) => {
  63 + if (err != null) {
  64 + this.wrieLog(" 错误" + id + ":" + err)
  65 + this.wrieLog(" 错误 stdout" + id + ":" + stdout)
  66 + this.wrieLog(" 错误 stderr" + id + ":" + stderr)
  67 + // 删除已存元素
  68 + classids.splice(classids.findIndex(item => item === id),1)
  69 + return
  70 + }
  71 + let files = fs.readdirSync(ymdDir);
  72 + if (files.indexOf(id + ".mp4") != -1) {
  73 + this.wrieLog(" 录制完成 课堂号:" + id)
  74 + // 删除已存元素
  75 + classids.splice(classids.findIndex(item => item === id),1)
  76 + }
  77 + })
  78 + }
  79 +
  80 + getConfigFileJson() {
  81 + const buffer = fs.readFileSync(process.cwd() + "/config/realTimeConfig.json")
  82 + return String(buffer)
  83 + }
  84 +}
  85 +
  86 +/**
  87 + * {
  88 + * "classId":[{ classId: '389675110', siteId: 'kuaikuenglish' }]
  89 + * }
  90 + */
  91 +router.post('/recording', function (req, res, next) {
  92 + new MediaCreat().wrieLog("录制启动:------>")
  93 + let fileConfig = new MediaCreat().getConfigFileJson()
  94 + if (!fileConfig) return false
  95 + /**
  96 + * classId:课堂号
  97 + * siteId:机构编码
  98 + * url:课堂地址
  99 + * duration:录制时长
  100 + * width height 录制视频宽高
  101 + * frameRate 帧率
  102 + * bitrate 码率
  103 + */
  104 + let { classId,siteId,url:classUrl,duration,width,height,frameRate,bitrate } = req.body
  105 +
  106 + if (classId && siteId && classUrl && duration && width && height && frameRate && bitrate) {
  107 + if(!Number.isInteger(classId) || !Number.isInteger(duration) || !Number.isInteger(width) || !Number.isInteger(height) || !Number.isInteger(frameRate) || !Number.isInteger(bitrate)){
  108 + res.send({ code: "1", message: "参数类型错误" });
  109 + return
  110 + }
  111 + let maxTime = 10*60*60*1000
  112 + if(duration < 0 || duration > maxTime){
  113 + res.send({ code: "1", message: "参数范围错误" });
  114 + return
  115 + }
  116 + // 避免多次重复课堂号请求
  117 + if(!classids.includes(classId)){
  118 + // 没有正在录制的该课堂
  119 + classids.push(classId)
  120 + bitrate = bitrate * 1024 // 转换 * 1024
  121 + new MediaCreat().recordingCreat(classId, siteId,classUrl,duration,width,height,frameRate,bitrate)
  122 + res.send({ code: "0" });
  123 + }else{
  124 + res.send({ code: "2", message: "重复请求课堂号" });
  125 + }
  126 + } else {
  127 + res.send({ code: "1", message: "缺少参数" });
  128 + }
  129 +
  130 +})
  131 +
  132 +
  133 +module.exports = router;
1 -var express = require('express');  
2 -var router = express.Router();  
3 -const { spawn } = require('child_process'); 1 +const express = require('express');
  2 +const router = express.Router();
  3 +const { spawn, exec } = require('child_process');
4 const fs = require("fs"); 4 const fs = require("fs");
  5 +var path = require('path')
  6 +const OSS = require('ali-oss');
  7 +require('dotenv').config(); // 加载环境变量
5 8
6 const method = require("../config/method") 9 const method = require("../config/method")
7 const config = require("../config/config") 10 const config = require("../config/config")
8 11
9 // const { GETCLASSURL, GETCLASSURLPARAMETER, PROJECTCATALOG, PROJECTWINCATALOG, BACKMEDIACONFIG } = config 12 // const { GETCLASSURL, GETCLASSURLPARAMETER, PROJECTCATALOG, PROJECTWINCATALOG, BACKMEDIACONFIG } = config
10 -const { YesterdayTime, getRequestClassIds,dayTimeYMD } = method 13 +const { YesterdayTime, getRequestClassIds, dayTimeYMD } = method
11 14
12 -const { startTime, endTime, ymd } = YesterdayTime()  
13 let siteIds = [] 15 let siteIds = []
14 16
15 let classid = [] 17 let classid = []
  18 +let classidPost = []
16 let parentData = {} 19 let parentData = {}
17 var classobj = {}; 20 var classobj = {};
18 let className = "" 21 let className = ""
  22 +let yesterday = "" // get写入课堂的时间
  23 +
  24 +// spawn("export DISPLAY=:7", { shell: true})
  25 +
19 /** 26 /**
20 - * 27 + *
21 * @param {*} id 课堂id 28 * @param {*} id 课堂id
22 */ 29 */
23 class MediaCreat { 30 class MediaCreat {
24 - constructor() { }  
25 - // 取出所有数据  
26 - async allData() {  
27 - let fileConfig = await new MediaCreat().getConfigFileJson()  
28 - if(!fileConfig) return false  
29 - className = siteIds.shift()  
30 - const { GETCLASSURL,GETCLASSURLPARAMETER } = JSON.parse(fileConfig)  
31 - let page = 1  
32 - if (className) {  
33 - let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, page)  
34 - parentData[result.data.data.siteId] = new Set()  
35 - for (let j = 0; j < result.data.data.list.length; j++) {  
36 - classid.push(result.data.data.list[j])  
37 - parentData[result.data.data.siteId].add(result.data.data.list[j]['classId'])  
38 - }  
39 - const { siteId, list, totalPage } = result.data.data  
40 - for (let i = page += 1; i <= totalPage; i++) {  
41 - let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, i)  
42 - for (let j = 0; j < result.data.data.list.length; j++) {  
43 - classid.push(result.data.data.list[j])  
44 - parentData[result.data.data.siteId].add(result.data.data.list[j]['classId'])  
45 - }  
46 - }  
47 - parentData[result.data.data.siteId] = Array.from(parentData[result.data.data.siteId])  
48 - if (siteIds.length) {  
49 - return await new MediaCreat().allData()  
50 - }  
51 - this.wrieLog("去重前的classId:------>" + JSON.stringify(classid))  
52 - this.wrieLog("httpData:------>" + JSON.stringify(result.data))  
53 - return true  
54 - } else {  
55 - return false 31 + constructor() {
56 } 32 }
57 - }  
58 - wrieLog(text){  
59 - // 写入log  
60 - let logFile = `./log/${dayTimeYMD().ymd}.txt`  
61 - fs.appendFileSync(logFile,new Date().toLocaleString() + " " +text+'\r\n');  
62 - }  
63 - async mediaCreat(id, siteId) {  
64 - let fileConfig = await new MediaCreat().getConfigFileJson()  
65 - if(!fileConfig) return false  
66 - const { BACKMEDIACONFIG,PROJECTWINCATALOG,PROJECTCATALOG } = JSON.parse(fileConfig)  
67 -  
68 - let mediaDir = PROJECTCATALOG + "/media/"  
69 - let classDir = PROJECTCATALOG + "/media/" + siteId  
70 - let ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + ymd  
71 - if (!fs.existsSync(mediaDir)) {  
72 - fs.mkdirSync(mediaDir);  
73 - }  
74 - if (!fs.existsSync(classDir)) {  
75 - fs.mkdirSync(classDir); 33 +
  34 + // 取出所有数据
  35 + async allData() {
  36 + const { startTime, endTime } = YesterdayTime()
  37 + let fileConfig = new MediaCreat().getConfigFileJson()
  38 + if (!fileConfig) return false
  39 + className = siteIds.shift()
  40 + const { GETCLASSURL, GETCLASSURLPARAMETER, classLastNumber } = JSON.parse(fileConfig)
  41 + let page = 1
  42 + if (className) {
  43 + let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, page)
  44 + // parentData[result.data.data.siteId] = new Set()
  45 + let resultList = result.data.data.list
  46 + for (let j = 0; j < resultList.length; j++) {
  47 + let item = resultList[j]
  48 + let classId = item['classId']
  49 + let number = classId.substr(classId.length - 1, 1)
  50 + if (classLastNumber.includes(number)){
  51 + classid.push(item)
  52 + // parentData[result.data.data.siteId].add(classId)
  53 + }
  54 + }
  55 + const { siteId, list, totalPage } = result.data.data
  56 + for (let i = page += 1; i <= totalPage; i++) {
  57 + let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, i)
  58 + let resultList = result.data.data.list
  59 + for (let j = 0; j < resultList.length; j++) {
  60 + let item = resultList[j]
  61 + let classId = item['classId']
  62 + let number = classId.substr(classId.length - 1, 1)
  63 + if (classLastNumber.includes(number)) {
  64 + classid.push(item)
  65 + // parentData[result.data.data.siteId].add(classId)
  66 + }
  67 + }
  68 + }
  69 + // parentData[result.data.data.siteId] = Array.from(parentData[result.data.data.siteId])
  70 + if (siteIds.length) {
  71 + return await new MediaCreat().allData()
  72 + }
  73 + this.wrieLog("去重前的classId:------>" + JSON.stringify(classid))
  74 + this.wrieLog("httpData:------>" + JSON.stringify(result.data))
  75 + return true
  76 + } else {
  77 + return false
  78 + }
76 } 79 }
77 - if (!fs.existsSync(ymdDir)) {  
78 - fs.mkdirSync(ymdDir); 80 +
  81 + wrieLog(text) {
  82 + // 写入log
  83 + let logFile = `./log/${dayTimeYMD().ymd}.txt`
  84 + fs.appendFileSync(logFile, new Date().toLocaleString() + " " + text + '\r\n');
79 } 85 }
80 - let url = `web_capture_c -o=../media/${siteId}/${ymd}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h}`  
81 - var workerProcess = spawn(url, { cwd: PROJECTWINCATALOG, shell: true })  
82 - // workerProcess.stdout.on('data', async function (data) {  
83 -  
84 - // });  
85 -  
86 - workerProcess.on('close', async (code) => {  
87 - let files = fs.readdirSync(ymdDir);  
88 - if (files.indexOf(id + ".mp4") == -1) {  
89 - //当前课堂没有写入到文件夹,进入重录  
90 - new MediaCreat().mediaCreat(id, siteId)  
91 - return false  
92 - } else {  
93 - if (parentData[siteId].indexOf(id.toString()) != -1) {  
94 - parentData[siteId].splice(parentData[siteId].indexOf(id.toString()), 1) 86 +
  87 + recordingCreat(id, siteId, type) {
  88 + this.wrieLog(" 课堂录制开始:------>" + id)
  89 + let fileConfig = this.getConfigFileJson()
  90 + if (!fileConfig) return false
  91 + const { BACKMEDIACONFIG, PROJECTWINCATALOG, PROJECTCATALOG } = JSON.parse(fileConfig)
  92 + let mediaDir = PROJECTCATALOG + "/media/"
  93 + let classDir = PROJECTCATALOG + "/media/" + siteId
  94 + let ymdDir = null
  95 + if (type == 'post') {
  96 + ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + dayTimeYMD().ymd
  97 + } else {
  98 + // get 的目录创建为上一天日期
  99 + ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + yesterday
95 } 100 }
96 - if (parentData[siteId].length == 0) {  
97 - // //全部录制完毕  
98 - this.wrieLog("录制结束:------>")  
99 - fs.writeFile(ymdDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {  
100 - if (err) {  
101 - console.log(err);  
102 - }  
103 - }); 101 +
  102 + if (!fs.existsSync(mediaDir)) {
  103 + fs.mkdirSync(mediaDir);
104 } 104 }
105 - if (classid.length) {  
106 - let shiftData = classid.shift()  
107 - new MediaCreat().mediaCreat(shiftData['classId'], shiftData['siteId']) 105 + if (!fs.existsSync(classDir)) {
  106 + fs.mkdirSync(classDir);
108 } 107 }
109 - }  
110 - });  
111 - }  
112 - async recordingCreat(id, siteId) {  
113 - let fileConfig = await new MediaCreat().getConfigFileJson()  
114 - if(!fileConfig) return false  
115 - const { BACKMEDIACONFIG,PROJECTWINCATALOG,PROJECTCATALOG } = JSON.parse(fileConfig)  
116 - let mediaDir = PROJECTCATALOG + "/media/"  
117 - let recordingDir = mediaDir + "recording/"  
118 - let classDir = recordingDir + siteId +"/"  
119 - let dateDir = classDir + dayTimeYMD().ymd  
120 - if (!fs.existsSync(mediaDir)) {  
121 - fs.mkdirSync(mediaDir);  
122 - }  
123 - if (!fs.existsSync(recordingDir)) {  
124 - fs.mkdirSync(recordingDir);  
125 - }  
126 - if (!fs.existsSync(classDir)) {  
127 - fs.mkdirSync(classDir);  
128 - }  
129 - if (!fs.existsSync(dateDir)) {  
130 - fs.mkdirSync(dateDir);  
131 - }  
132 - let url = `web_capture_c -o=../media/recording/${siteId}/${dayTimeYMD().ymd}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h}`  
133 -  
134 - var workerProcess = spawn(url, { cwd: PROJECTWINCATALOG, shell: true })  
135 - // workerProcess.stdout.on('data', async function (data) {  
136 -  
137 - // });  
138 -  
139 - workerProcess.on('close', async (code) =>{  
140 - let files = fs.readdirSync(dateDir);  
141 - if (files.indexOf(id + ".mp4") == -1) {  
142 - //当前课堂没有写入到文件夹,进入重录  
143 - new MediaCreat().recordingCreat(id, siteId)  
144 - return false  
145 - } else {  
146 - if (parentData[siteId].indexOf(id.toString()) != -1) {  
147 - parentData[siteId].splice(parentData[siteId].indexOf(id.toString()), 1) 108 + if (!fs.existsSync(ymdDir)) {
  109 + fs.mkdirSync(ymdDir);
148 } 110 }
149 - if (parentData[siteId].length == 0) {  
150 - // //全部录制完毕  
151 - this.wrieLog("录制结束:------>")  
152 - fs.writeFile(dateDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {  
153 - if (err) {  
154 - console.log(err); 111 + let files = fs.readdirSync(ymdDir);
  112 + // let interValGetFile = setInterval(()=>{
  113 + this.wrieLog("ymdDir" + ymdDir)
  114 + this.wrieLog("files:" + files)
  115 + if (files.indexOf(id + ".mp4") != -1) {
  116 + this.wrieLog("已存在:" + id + "课堂号,停止继续录制")
  117 + if (type == 'post') {
  118 + if (classidPost.length) {
  119 + let shiftData = classidPost.shift()
  120 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  121 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  122 + }
  123 + } else {
  124 + if (classid.length) {
  125 + let shiftData = classid.shift()
  126 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  127 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  128 + }
155 } 129 }
156 - });  
157 - }  
158 - if (classid.length) {  
159 - let shiftData = classid.shift()  
160 - new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId']) 130 + return
161 } 131 }
162 - }  
163 - });  
164 - }  
165 -  
166 - async getConfigFileJson(){  
167 - const buffer= fs.readFileSync(process.cwd()+"/config/config.json")  
168 - return String(buffer)  
169 - } 132 + // 目前url是linux的写法 win系统不支持
  133 + // export DISPLAY=:7
  134 + // let url = `${path.resolve(__dirname, PROJECTWINCATALOG+"/web_capture_c")} -o=${ymdDir}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h}`
  135 + // console.log("url", url)
  136 + let url = `${PROJECTWINCATALOG}/web_capture_c -o=${ymdDir}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}&language=zh-cn" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h}`
  137 + this.wrieLog(" 启动url " + url)
  138 + exec(url, { maxBuffer: 1073741824 }, (err, stdout, stderr) => {
  139 + if (err != null) {
  140 + this.wrieLog(" 错误" + id + ":" + err)
  141 + this.wrieLog(" 错误 stdout" + id + ":" + stdout)
  142 + this.wrieLog(" 错误 stderr" + id + ":" + stderr)
  143 + return
  144 + }
  145 + let files = fs.readdirSync(ymdDir);
  146 + // let interValGetFile = setInterval(()=>{
  147 + if (files.indexOf(id + ".mp4") == -1) {
  148 + this.wrieLog(" 课堂录制未发现该" + id + "课堂号")
  149 + } else {
  150 + if (type == 'get') {
  151 + if (classid.length) {
  152 + let shiftData = classid.shift()
  153 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  154 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  155 + } else {
  156 + this.wrieLog("录制结束:------>")
  157 + fs.writeFile(ymdDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {
  158 + if (err) {
  159 + console.log(err);
  160 + }
  161 + });
  162 + }
  163 + } else {
  164 + if (classidPost.length) {
  165 + let shiftData = classidPost.shift()
  166 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  167 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  168 + } else {
  169 + this.wrieLog("录制结束:------>")
  170 + fs.writeFile(ymdDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {
  171 + if (err) {
  172 + console.log(err);
  173 + }
  174 + });
  175 + }
  176 + }
  177 +
  178 + }
  179 + })
  180 + }
  181 +
  182 + getConfigFileJson() {
  183 + const buffer = fs.readFileSync(process.cwd() + "/config/config.json")
  184 + return String(buffer)
  185 + }
170 } 186 }
171 187
172 188
173 -router.get('/', async function (req, res, next) {  
174 - new MediaCreat().wrieLog("录制开始:------>")  
175 - let fileConfig = await new MediaCreat().getConfigFileJson()  
176 - if(!fileConfig) return false  
177 -  
178 - const {GETCLASSURLPARAMETER} = JSON.parse(fileConfig)  
179 - siteIds = GETCLASSURLPARAMETER.siteId  
180 -  
181 - let result = await new MediaCreat().allData()  
182 - if (result) {  
183 - // 去重  
184 - classobj = {}  
185 - classid = classid.reduce(function (item, next) {  
186 - classobj[next.classId] ? '' : classobj[next.classId] = true && item.push(next);  
187 - return item;  
188 - }, []);  
189 - // 写入log  
190 - new MediaCreat().wrieLog("去重后的classid:------>" + JSON.stringify(classid))  
191 - if (classid.length) {  
192 - for (let i = 0; i < GETCLASSURLPARAMETER.maxMedia; i++) {  
193 - let shiftData = classid.shift()  
194 - if (shiftData) {  
195 - new MediaCreat().mediaCreat(shiftData['classId'], shiftData['siteId']) 189 +router.get('/recording', async function (req, res, next) {
  190 + if (classid.length > 0) {
  191 + // 有正在录制中的课堂,禁止重复
  192 + res.send({ code: "1", message: "有正在录制中的课堂", data: classid });
  193 + return
  194 + }
  195 + new MediaCreat().wrieLog("脚本录制开始:------>")
  196 + let fileConfig = new MediaCreat().getConfigFileJson()
  197 + if (!fileConfig) return false
  198 +
  199 + const { GETCLASSURLPARAMETER } = JSON.parse(fileConfig)
  200 + siteIds = GETCLASSURLPARAMETER.siteId
  201 + if (siteIds.length == 0) {
  202 + new MediaCreat().wrieLog("脚本录制,未配置siteId:------>" + siteIds)
  203 + res.send({ code: "1", message: "未配置siteId", data: siteIds });
  204 + return
  205 + }
  206 + let result = await new MediaCreat().allData()
  207 + if (result) {
  208 + // 去重
  209 + classobj = {}
  210 + classid = classid.reduce(function (item, next) {
  211 + classobj[next.classId] ? '' : classobj[next.classId] = true && item.push(next);
  212 + return item;
  213 + }, []);
  214 + // 写入log
  215 + new MediaCreat().wrieLog("去重后的classid:------>" + JSON.stringify(classid))
  216 + if (classid.length) {
  217 + for (let i = 0; i < GETCLASSURLPARAMETER.maxMedia; i++) {
  218 + let shiftData = classid.shift()
  219 + if (shiftData) {
  220 + yesterday = YesterdayTime().ymd
  221 + new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId'], 'get')
  222 + } else {
  223 + return false
  224 + }
  225 + }
  226 + res.send({ code: "0" });
196 } else { 227 } else {
197 - return false 228 + res.send({ code: "1", message: "无录制数据" });
198 } 229 }
199 - }  
200 - res.send({ code: "0" });  
201 - }else{  
202 - res.send({ code: "1" ,message:"无录制数据"}); 230 + } else {
  231 + res.send({ code: "1", message: "无录制数据" });
203 } 232 }
204 - }  
205 }); 233 });
206 234
207 /** 235 /**
@@ -210,33 +238,103 @@ router.get('/', async function (req, res, next) { @@ -210,33 +238,103 @@ router.get('/', async function (req, res, next) {
210 * } 238 * }
211 */ 239 */
212 router.post('/recording', async function (req, res, next) { 240 router.post('/recording', async function (req, res, next) {
213 - new MediaCreat().wrieLog("录制开始:------>")  
214 - let fileConfig = await new MediaCreat().getConfigFileJson()  
215 - if(!fileConfig) return false  
216 - const { classId } = req.body  
217 - if(classId && classId.length){  
218 - classid = classId  
219 - const { PROJECTCATALOG,GETCLASSURLPARAMETER } = JSON.parse(fileConfig)  
220 - for(let i=0;i<classId.length;i++){  
221 - if(!Array.isArray(parentData[classId[i].siteId])){  
222 - parentData[classId[i].siteId] = []  
223 - }  
224 - parentData[classId[i].siteId].push(classId[i].classId) 241 + new MediaCreat().wrieLog("录制启动:------>")
  242 + let fileConfig = new MediaCreat().getConfigFileJson()
  243 + if (!fileConfig) return false
  244 + let { classId, maxMedia } = req.body
  245 + if (classidPost.length > 0) {
  246 + // 有正在录制中的课堂,禁止重复
  247 + res.send({ code: "1", message: "有正在录制中的课堂", data: classidPost });
  248 + return
225 } 249 }
226 - for(let i=0;i<GETCLASSURLPARAMETER.maxMedia;i++){  
227 - let shiftData = classid.shift()  
228 - if (shiftData) {  
229 - new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId'])  
230 - } else {  
231 - return false  
232 - } 250 + if (classId && classId.length) {
  251 + classidPost = classId
  252 + if (!maxMedia || maxMedia == undefined) {
  253 + maxMedia = 1
  254 + }
  255 + for (let i = 0; i < maxMedia; i++) {
  256 + let shiftData = classidPost.shift()
  257 + if (shiftData) {
  258 + new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId'], 'post')
  259 + } else {
  260 + return false
  261 + }
  262 + }
  263 + res.send({ code: "0" });
  264 + } else {
  265 + res.send({ code: "1", message: "无录制数据" });
233 } 266 }
234 - res.send({ code: "0" });  
235 - }else{  
236 - res.send({ code: "1" ,message:"无录制数据"});  
237 - }  
238 - 267 +
239 }) 268 })
240 269
  270 +// 判断该视频文件是否存在
  271 +router.post('/fileExists', async (req, res) => {
  272 + const { siteId, classId, classStartTime } = req.body;
  273 +
  274 + // 检查输入参数
  275 + if (!siteId) {
  276 + return res.status(400).send({ code: 2, message: "机构编码无效" });
  277 + }
  278 + if (!classId) {
  279 + return res.status(400).send({ code: 3, message: "课堂号无效" });
  280 + }
  281 +
  282 + try {
  283 + // 检查环境变量是否设置
  284 + if (!process.env.ALIBABA_CLOUD_ACCESS_KEY_ID || !process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET) {
  285 + throw new Error('Environment variables are not set correctly.');
  286 + }
  287 +
  288 + // 创建OSS客户端实例
  289 + const client = new OSS({
  290 + region: 'oss-cn-beijing',
  291 + accessKeyId: process.env.ALIBABA_CLOUD_ACCESS_KEY_ID,
  292 + accessKeySecret: process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
  293 + authorizationV4: true,
  294 + bucket: 'xdymp4'
  295 + });
  296 +
  297 + // 构建文件前缀
  298 + let prefix = '';
  299 + if (classStartTime) {
  300 + prefix = `oss/${siteId}/${classStartTime}/${classId}.mp4`;
  301 + } else {
  302 + // 如果没有提供classStartTime,默认返回文件未生成
  303 + let classUrl = `https://pclive.xuedianyun.com/pcBase/pclive2/release/index.html?portalIP=saas.xuedianyun.com&portalPort=80&classId=${classId}&channels=2&playRecord=1#/`;
  304 + return res.send({ code: 1, message: "文件未生成", classUrl });
  305 + }
  306 +
  307 + // 使用 await 处理异步操作
  308 + const result = await client.list({ prefix });
  309 + console.log('OSS list result:', JSON.stringify(result, null, 2)); // 日志记录详细结果
  310 +
  311 + // 检查是否有匹配的文件
  312 + let isVideo = false;
  313 + let url = "";
  314 + if (result.res.status === 200 && result.objects && result.objects.length > 0) {
  315 + for (let item of result.objects) {
  316 + // 更精确的匹配逻辑,确保找到的是目标文件
  317 + if (item.name === `${prefix}`) {
  318 + isVideo = true;
  319 + url = item.name;
  320 + break;
  321 + }
  322 + }
  323 + }
  324 +
  325 + // 根据结果发送响应
  326 + if (!isVideo) {
  327 + let classUrl = `https://pclive.xuedianyun.com/pcBase/pclive2/release/index.html?portalIP=saas.xuedianyun.com&portalPort=80&classId=${classId}&channels=2&playRecord=1#/`;
  328 + res.send({ code: 1, message: "文件未生成", classUrl });
  329 + } else {
  330 + let classUrl = `https://xdymp4.xuedianyun.com/${url}`;
  331 + res.send({ code: 0, message: "文件已生成", classUrl });
  332 + }
  333 +
  334 + } catch (err) {
  335 + console.error('Error checking file existence:', err);
  336 + res.status(500).send({ code: -1, message: "服务器内部错误", error: err.message });
  337 + }
  338 +});
241 339
242 -module.exports = router; 340 +module.exports = router
  1 +var express = require('express');
  2 +var router = express.Router();
  3 +const { spawn, exec } = require('child_process');
  4 +const fs = require("fs");
  5 +var path = require('path')
  6 +var ObsClient = require('esdk-obs-nodejs');
  7 +
  8 +const method = require("../config/method")
  9 +const config = require("../config/config")
  10 +
  11 +// const { GETCLASSURL, GETCLASSURLPARAMETER, PROJECTCATALOG, PROJECTWINCATALOG, BACKMEDIACONFIG } = config
  12 +const { YesterdayTime, getRequestClassIds, dayTimeYMD } = method
  13 +
  14 +let siteIds = []
  15 +
  16 +let classid = []
  17 +let classidPost = []
  18 +let parentData = {}
  19 +var classobj = {};
  20 +let className = ""
  21 +
  22 +// spawn("export DISPLAY=:7", { shell: true})
  23 +
  24 +/**
  25 + *
  26 + * @param {*} id 课堂id
  27 + */
  28 +class MediaCreat {
  29 + constructor() {
  30 + }
  31 +
  32 + // 取出所有数据
  33 + async allData() {
  34 + const { startTime, endTime } = YesterdayTime()
  35 + let fileConfig = new MediaCreat().getConfigFileJson()
  36 + if (!fileConfig) return false
  37 + className = siteIds.shift()
  38 + const { GETCLASSURL, GETCLASSURLPARAMETER } = JSON.parse(fileConfig)
  39 + let page = 1
  40 + if (className) {
  41 + let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, page)
  42 + parentData[result.data.data.siteId] = new Set()
  43 + for (let j = 0; j < result.data.data.list.length; j++) {
  44 + classid.push(result.data.data.list[j])
  45 + parentData[result.data.data.siteId].add(result.data.data.list[j]['classId'])
  46 + }
  47 + const { siteId, list, totalPage } = result.data.data
  48 + for (let i = page += 1; i <= totalPage; i++) {
  49 + let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, i)
  50 + for (let j = 0; j < result.data.data.list.length; j++) {
  51 + classid.push(result.data.data.list[j])
  52 + parentData[result.data.data.siteId].add(result.data.data.list[j]['classId'])
  53 + }
  54 + }
  55 + parentData[result.data.data.siteId] = Array.from(parentData[result.data.data.siteId])
  56 + if (siteIds.length) {
  57 + return await new MediaCreat().allData()
  58 + }
  59 + this.wrieLog("去重前的classId:------>" + JSON.stringify(classid))
  60 + this.wrieLog("httpData:------>" + JSON.stringify(result.data))
  61 + return true
  62 + } else {
  63 + return false
  64 + }
  65 + }
  66 +
  67 + wrieLog(text) {
  68 + // 写入log
  69 + let logFile = `./log/${dayTimeYMD().ymd}.txt`
  70 + fs.appendFileSync(logFile, new Date().toLocaleString() + " " + text + '\r\n');
  71 + }
  72 +
  73 + recordingCreat(id, siteId, type) {
  74 + this.wrieLog(" 课堂录制开始:------>" + id)
  75 + let fileConfig = this.getConfigFileJson()
  76 + if (!fileConfig) return false
  77 + const { BACKMEDIACONFIG, PROJECTWINCATALOG, PROJECTCATALOG } = JSON.parse(fileConfig)
  78 + let mediaDir = PROJECTCATALOG + "/media/"
  79 + let classDir = PROJECTCATALOG + "/media/" + siteId
  80 + let ymdDir = null
  81 + if (type == 'post') {
  82 + ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + dayTimeYMD().ymd
  83 + } else {
  84 + // get 的目录创建为上一天日期
  85 + ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + YesterdayTime().ymd
  86 + }
  87 +
  88 + if (!fs.existsSync(mediaDir)) {
  89 + fs.mkdirSync(mediaDir);
  90 + }
  91 + if (!fs.existsSync(classDir)) {
  92 + fs.mkdirSync(classDir);
  93 + }
  94 + if (!fs.existsSync(ymdDir)) {
  95 + fs.mkdirSync(ymdDir);
  96 + }
  97 + let files = fs.readdirSync(ymdDir);
  98 + // let interValGetFile = setInterval(()=>{
  99 + this.wrieLog("files:" + files)
  100 + if (files.indexOf(id + ".mp4") != -1) {
  101 + this.wrieLog("已存在:" + id + "课堂号,停止继续录制")
  102 + if (type == 'post') {
  103 + if (classidPost.length) {
  104 + let shiftData = classidPost.shift()
  105 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  106 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  107 + }
  108 + } else {
  109 + if (classid.length) {
  110 + let shiftData = classid.shift()
  111 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  112 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  113 + }
  114 + }
  115 + return
  116 + }
  117 + // 目前url是linux的写法 win系统不支持
  118 + // export DISPLAY=:7
  119 + // let url = `${path.resolve(__dirname, PROJECTWINCATALOG+"/web_capture_c")} -o=${ymdDir}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h}`
  120 + // console.log("url", url)
  121 + let url = `${PROJECTWINCATALOG}/web_capture_c -o=${ymdDir}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}&language=zh-cn" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h} -vb=999999999999`
  122 + exec(url, { maxBuffer: 1073741824 }, (err, stdout, stderr) => {
  123 + if (err != null) {
  124 + this.wrieLog(" 错误" + id + ":" + err)
  125 + this.wrieLog(" 错误 stdout" + id + ":" + stdout)
  126 + this.wrieLog(" 错误 stderr" + id + ":" + stderr)
  127 + return
  128 + }
  129 + let files = fs.readdirSync(ymdDir);
  130 + // let interValGetFile = setInterval(()=>{
  131 + if (files.indexOf(id + ".mp4") == -1) {
  132 + this.wrieLog(" 课堂录制未发现该" + id + "课堂号")
  133 + } else {
  134 + if (type == 'get') {
  135 + if (classid.length) {
  136 + let shiftData = classid.shift()
  137 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  138 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  139 + } else {
  140 + this.wrieLog("录制结束:------>")
  141 + fs.writeFile(ymdDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {
  142 + if (err) {
  143 + console.log(err);
  144 + }
  145 + });
  146 + }
  147 + } else {
  148 + if (classidPost.length) {
  149 + let shiftData = classidPost.shift()
  150 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  151 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  152 + } else {
  153 + this.wrieLog("录制结束:------>")
  154 + fs.writeFile(ymdDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {
  155 + if (err) {
  156 + console.log(err);
  157 + }
  158 + });
  159 + }
  160 + }
  161 +
  162 + }
  163 + })
  164 + }
  165 +
  166 + getConfigFileJson() {
  167 + const buffer = fs.readFileSync(process.cwd() + "/config/config.json")
  168 + return String(buffer)
  169 + }
  170 +}
  171 +
  172 +
  173 +router.get('/recording', async function (req, res, next) {
  174 + if (classid.length > 0) {
  175 + // 有正在录制中的课堂,禁止重复
  176 + res.send({ code: "1", message: "有正在录制中的课堂", data: classid });
  177 + return
  178 + }
  179 + new MediaCreat().wrieLog("脚本录制开始:------>")
  180 + let fileConfig = new MediaCreat().getConfigFileJson()
  181 + if (!fileConfig) return false
  182 +
  183 + const { GETCLASSURLPARAMETER } = JSON.parse(fileConfig)
  184 + siteIds = GETCLASSURLPARAMETER.siteId
  185 + if (siteIds.length == 0) {
  186 + new MediaCreat().wrieLog("脚本录制,未配置siteId:------>" + siteIds)
  187 + res.send({ code: "1", message: "未配置siteId", data: siteIds });
  188 + return
  189 + }
  190 + let result = await new MediaCreat().allData()
  191 + if (result) {
  192 + // 去重
  193 + classobj = {}
  194 + classid = classid.reduce(function (item, next) {
  195 + classobj[next.classId] ? '' : classobj[next.classId] = true && item.push(next);
  196 + return item;
  197 + }, []);
  198 + // 写入log
  199 + new MediaCreat().wrieLog("去重后的classid:------>" + JSON.stringify(classid))
  200 + if (classid.length) {
  201 + for (let i = 0; i < GETCLASSURLPARAMETER.maxMedia; i++) {
  202 + let shiftData = classid.shift()
  203 + if (shiftData) {
  204 + new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId'], 'get')
  205 + } else {
  206 + return false
  207 + }
  208 + }
  209 + res.send({ code: "0" });
  210 + } else {
  211 + res.send({ code: "1", message: "无录制数据" });
  212 + }
  213 + } else {
  214 + res.send({ code: "1", message: "无录制数据" });
  215 + }
  216 +});
  217 +
  218 +/**
  219 + * {
  220 + * "classId":[{ classId: '389675110', siteId: 'kuaikuenglish' }]
  221 + * }
  222 + */
  223 +router.post('/recording', async function (req, res, next) {
  224 + new MediaCreat().wrieLog("录制启动:------>")
  225 + let fileConfig = new MediaCreat().getConfigFileJson()
  226 + if (!fileConfig) return false
  227 + let { classId, maxMedia } = req.body
  228 + if (classidPost.length > 0) {
  229 + // 有正在录制中的课堂,禁止重复
  230 + res.send({ code: "1", message: "有正在录制中的课堂", data: classidPost });
  231 + return
  232 + }
  233 + if (classId && classId.length) {
  234 + classidPost = classId
  235 + if (!maxMedia || maxMedia == undefined) {
  236 + maxMedia = 1
  237 + }
  238 + for (let i = 0; i < maxMedia; i++) {
  239 + let shiftData = classidPost.shift()
  240 + if (shiftData) {
  241 + new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId'], 'post')
  242 + } else {
  243 + return false
  244 + }
  245 + }
  246 + res.send({ code: "0" });
  247 + } else {
  248 + res.send({ code: "1", message: "无录制数据" });
  249 + }
  250 +
  251 +})
  252 +
  253 +// 判断该视频文件是否存在
  254 +router.post('/fileExists', async function (req, res, next) {
  255 + const body = req.body
  256 + if(!body.siteId) {
  257 + res.send({ code: 2,message:"机构编码无效" });
  258 + return
  259 + }
  260 + if(!body.classId) {
  261 + res.send({ code: 3,message:"课堂号无效" });
  262 + return
  263 + }
  264 +
  265 + // 创建ObsClient实例
  266 + var obsClient = new ObsClient({
  267 + access_key_id: 'FY27OD11ZJVC385AOTW9',
  268 + secret_access_key: 'ZqnfRjaseCtuRbE79GvqkPiYUdT83ZbB0oU7fBo3',
  269 + server : 'obs.cn-north-4.myhuaweicloud.com'
  270 + });
  271 +
  272 + let isVideo = false
  273 + let url = ""
  274 + obsClient.listObjects({
  275 + Bucket : 'xdymp4'
  276 + }, (err, result) => {
  277 + if(err){
  278 + console.error('Error-->' + err);
  279 + }else{
  280 + if(result.CommonMsg.Status == 200){
  281 + for(let i=0;i<result.InterfaceResult.Contents.length;i++){
  282 + let item = result.InterfaceResult.Contents[i]
  283 + if(item.Key && item.Key.indexOf(body.classId) > -1){
  284 + isVideo = true
  285 + url = item.Key
  286 + break
  287 + }
  288 + }
  289 + }
  290 + }
  291 + if(!isVideo){
  292 + let classUrl = `https://pclive.xuedianyun.com/pcBase/pclive2/release/index.html?portalIP=saas.xuedianyun.com&portalPort=80&classId=${body.classId}&channels=2&playRecord=1#/`
  293 + res.send({ code: 1,message:"文件未生成",classUrl });
  294 + }else{
  295 + let classUrl = `https://xdymp4.xuedianyun.com/${url}`
  296 + res.send({ code: 0,message:"文件已生成",classUrl });
  297 + }
  298 + // 关闭obsClient
  299 + obsClient.close();
  300 + });
  301 +})
  302 +
  303 +module.exports = router;
  1 +var express = require('express');
  2 +var router = express.Router();
  3 +const { spawn, exec } = require('child_process');
  4 +const fs = require("fs");
  5 +var path = require('path')
  6 +var ObsClient = require('esdk-obs-nodejs');
  7 +
  8 +const method = require("../config/method")
  9 +const config = require("../config/config")
  10 +
  11 +// const { GETCLASSURL, GETCLASSURLPARAMETER, PROJECTCATALOG, PROJECTWINCATALOG, BACKMEDIACONFIG } = config
  12 +const { YesterdayTime, getRequestClassIds, dayTimeYMD } = method
  13 +
  14 +let siteIds = []
  15 +
  16 +let classid = []
  17 +let classidPost = []
  18 +let parentData = {}
  19 +var classobj = {};
  20 +let className = ""
  21 +let yesterday = "" // get写入课堂的时间
  22 +
  23 +// spawn("export DISPLAY=:7", { shell: true})
  24 +
  25 +/**
  26 + *
  27 + * @param {*} id 课堂id
  28 + */
  29 +class MediaCreat {
  30 + constructor() {
  31 + }
  32 +
  33 + // 取出所有数据
  34 + async allData() {
  35 + const { startTime, endTime } = YesterdayTime()
  36 + let fileConfig = new MediaCreat().getConfigFileJson()
  37 + if (!fileConfig) return false
  38 + className = siteIds.shift()
  39 + const { GETCLASSURL, GETCLASSURLPARAMETER, classLastNumber } = JSON.parse(fileConfig)
  40 + let page = 1
  41 + if (className) {
  42 + let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, page)
  43 + // parentData[result.data.data.siteId] = new Set()
  44 + let resultList = result.data.data.list
  45 + for (let j = 0; j < resultList.length; j++) {
  46 + let item = resultList[j]
  47 + let classId = item['classId']
  48 + let number = classId.substr(classId.length - 1, 1)
  49 + if (classLastNumber.includes(number)){
  50 + classid.push(item)
  51 + // parentData[result.data.data.siteId].add(classId)
  52 + }
  53 + }
  54 + const { siteId, list, totalPage } = result.data.data
  55 + for (let i = page += 1; i <= totalPage; i++) {
  56 + let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, i)
  57 + let resultList = result.data.data.list
  58 + for (let j = 0; j < resultList.length; j++) {
  59 + let item = resultList[j]
  60 + let classId = item['classId']
  61 + let number = classId.substr(classId.length - 1, 1)
  62 + if (classLastNumber.includes(number)) {
  63 + classid.push(item)
  64 + // parentData[result.data.data.siteId].add(classId)
  65 + }
  66 + }
  67 + }
  68 + // parentData[result.data.data.siteId] = Array.from(parentData[result.data.data.siteId])
  69 + if (siteIds.length) {
  70 + return await new MediaCreat().allData()
  71 + }
  72 + this.wrieLog("去重前的classId:------>" + JSON.stringify(classid))
  73 + this.wrieLog("httpData:------>" + JSON.stringify(result.data))
  74 + return true
  75 + } else {
  76 + return false
  77 + }
  78 + }
  79 +
  80 + wrieLog(text) {
  81 + // 写入log
  82 + let logFile = `./log/${dayTimeYMD().ymd}.txt`
  83 + fs.appendFileSync(logFile, new Date().toLocaleString() + " " + text + '\r\n');
  84 + }
  85 +
  86 + recordingCreat(id, siteId, type) {
  87 + this.wrieLog(" 课堂录制开始:------>" + id)
  88 + let fileConfig = this.getConfigFileJson()
  89 + if (!fileConfig) return false
  90 + const { BACKMEDIACONFIG, PROJECTWINCATALOG, PROJECTCATALOG } = JSON.parse(fileConfig)
  91 + let mediaDir = PROJECTCATALOG + "/media/"
  92 + let classDir = PROJECTCATALOG + "/media/" + siteId
  93 + let ymdDir = null
  94 + if (type == 'post') {
  95 + ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + dayTimeYMD().ymd
  96 + } else {
  97 + // get 的目录创建为上一天日期
  98 + ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + yesterday
  99 + }
  100 +
  101 + if (!fs.existsSync(mediaDir)) {
  102 + fs.mkdirSync(mediaDir);
  103 + }
  104 + if (!fs.existsSync(classDir)) {
  105 + fs.mkdirSync(classDir);
  106 + }
  107 + if (!fs.existsSync(ymdDir)) {
  108 + fs.mkdirSync(ymdDir);
  109 + }
  110 + let files = fs.readdirSync(ymdDir);
  111 + // let interValGetFile = setInterval(()=>{
  112 + this.wrieLog("ymdDir" + ymdDir)
  113 + this.wrieLog("files:" + files)
  114 + if (files.indexOf(id + ".mp4") != -1) {
  115 + this.wrieLog("已存在:" + id + "课堂号,停止继续录制")
  116 + if (type == 'post') {
  117 + if (classidPost.length) {
  118 + let shiftData = classidPost.shift()
  119 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  120 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  121 + }
  122 + } else {
  123 + if (classid.length) {
  124 + let shiftData = classid.shift()
  125 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  126 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  127 + }
  128 + }
  129 + return
  130 + }
  131 + // 目前url是linux的写法 win系统不支持
  132 + // export DISPLAY=:7
  133 + // let url = `${path.resolve(__dirname, PROJECTWINCATALOG+"/web_capture_c")} -o=${ymdDir}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h}`
  134 + // console.log("url", url)
  135 + let url = `${PROJECTWINCATALOG}/web_capture_c -o=${ymdDir}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}&language=zh-tw" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h}`
  136 + this.wrieLog(" 启动url " + url)
  137 + exec(url, { maxBuffer: 1073741824 }, (err, stdout, stderr) => {
  138 + if (err != null) {
  139 + this.wrieLog(" 错误" + id + ":" + err)
  140 + this.wrieLog(" 错误 stdout" + id + ":" + stdout)
  141 + this.wrieLog(" 错误 stderr" + id + ":" + stderr)
  142 + return
  143 + }
  144 + let files = fs.readdirSync(ymdDir);
  145 + // let interValGetFile = setInterval(()=>{
  146 + if (files.indexOf(id + ".mp4") == -1) {
  147 + this.wrieLog(" 课堂录制未发现该" + id + "课堂号")
  148 + } else {
  149 + if (type == 'get') {
  150 + if (classid.length) {
  151 + let shiftData = classid.shift()
  152 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  153 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  154 + } else {
  155 + this.wrieLog("录制结束:------>")
  156 + fs.writeFile(ymdDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {
  157 + if (err) {
  158 + console.log(err);
  159 + }
  160 + });
  161 + }
  162 + } else {
  163 + if (classidPost.length) {
  164 + let shiftData = classidPost.shift()
  165 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  166 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  167 + } else {
  168 + this.wrieLog("录制结束:------>")
  169 + fs.writeFile(ymdDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {
  170 + if (err) {
  171 + console.log(err);
  172 + }
  173 + });
  174 + }
  175 + }
  176 +
  177 + }
  178 + })
  179 + }
  180 +
  181 + getConfigFileJson() {
  182 + const buffer = fs.readFileSync(process.cwd() + "/config/config.json")
  183 + return String(buffer)
  184 + }
  185 +}
  186 +
  187 +
  188 +router.get('/recording', async function (req, res, next) {
  189 + if (classid.length > 0) {
  190 + // 有正在录制中的课堂,禁止重复
  191 + res.send({ code: "1", message: "有正在录制中的课堂", data: classid });
  192 + return
  193 + }
  194 + new MediaCreat().wrieLog("脚本录制开始:------>")
  195 + let fileConfig = new MediaCreat().getConfigFileJson()
  196 + if (!fileConfig) return false
  197 +
  198 + const { GETCLASSURLPARAMETER } = JSON.parse(fileConfig)
  199 + siteIds = GETCLASSURLPARAMETER.siteId
  200 + if (siteIds.length == 0) {
  201 + new MediaCreat().wrieLog("脚本录制,未配置siteId:------>" + siteIds)
  202 + res.send({ code: "1", message: "未配置siteId", data: siteIds });
  203 + return
  204 + }
  205 + let result = await new MediaCreat().allData()
  206 + if (result) {
  207 + // 去重
  208 + classobj = {}
  209 + classid = classid.reduce(function (item, next) {
  210 + classobj[next.classId] ? '' : classobj[next.classId] = true && item.push(next);
  211 + return item;
  212 + }, []);
  213 + // 写入log
  214 + new MediaCreat().wrieLog("去重后的classid:------>" + JSON.stringify(classid))
  215 + if (classid.length) {
  216 + for (let i = 0; i < GETCLASSURLPARAMETER.maxMedia; i++) {
  217 + let shiftData = classid.shift()
  218 + if (shiftData) {
  219 + yesterday = YesterdayTime().ymd
  220 + new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId'], 'get')
  221 + } else {
  222 + return false
  223 + }
  224 + }
  225 + res.send({ code: "0" });
  226 + } else {
  227 + res.send({ code: "1", message: "无录制数据" });
  228 + }
  229 + } else {
  230 + res.send({ code: "1", message: "无录制数据" });
  231 + }
  232 +});
  233 +
  234 +/**
  235 + * {
  236 + * "classId":[{ classId: '389675110', siteId: 'kuaikuenglish' }]
  237 + * }
  238 + */
  239 +router.post('/recording', async function (req, res, next) {
  240 + new MediaCreat().wrieLog("录制启动:------>")
  241 + let fileConfig = new MediaCreat().getConfigFileJson()
  242 + if (!fileConfig) return false
  243 + let { classId, maxMedia } = req.body
  244 + if (classidPost.length > 0) {
  245 + // 有正在录制中的课堂,禁止重复
  246 + res.send({ code: "1", message: "有正在录制中的课堂", data: classidPost });
  247 + return
  248 + }
  249 + if (classId && classId.length) {
  250 + classidPost = classId
  251 + if (!maxMedia || maxMedia == undefined) {
  252 + maxMedia = 1
  253 + }
  254 + for (let i = 0; i < maxMedia; i++) {
  255 + let shiftData = classidPost.shift()
  256 + if (shiftData) {
  257 + new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId'], 'post')
  258 + } else {
  259 + return false
  260 + }
  261 + }
  262 + res.send({ code: "0" });
  263 + } else {
  264 + res.send({ code: "1", message: "无录制数据" });
  265 + }
  266 +
  267 +})
  268 +
  269 +// 判断该视频文件是否存在
  270 +router.post('/fileExists', async function (req, res, next) {
  271 + const body = req.body
  272 + if (!body.siteId) {
  273 + res.send({code: 2, message: "机构编码无效"});
  274 + return
  275 + }
  276 + if (!body.classId) {
  277 + res.send({code: 3, message: "课堂号无效"});
  278 + return
  279 + }
  280 +
  281 + // if(!body.classStartTime) {
  282 + // res.send({ code: 4,message:"时间不存在" });
  283 + // return
  284 + // }
  285 +
  286 + // 创建ObsClient实例
  287 + var obsClient = new ObsClient({
  288 + access_key_id: 'LHFQDCSI4AQVDMEBI5TQ',
  289 + secret_access_key: '6iPZ2RM41cAFUvAmTSrjRS4vgjAP0bnwM9kiCXIk',
  290 + server: 'obs.cn-north-4.myhuaweicloud.com'
  291 + });
  292 +
  293 + let isVideo = false
  294 + let url = ""
  295 + let Prefix = ""
  296 + if (body.classStartTime) {
  297 + Prefix = `oss/${body.siteId}/${body.classStartTime}/${body.classId}.mp4`
  298 + obsClient.listObjects({
  299 + Bucket: 'mp4record',
  300 + Prefix: Prefix,
  301 + // MaxKeys:1
  302 + }, (err, result) => {
  303 + if (err) {
  304 + console.error('Error-->' + err);
  305 + } else {
  306 + if (result.CommonMsg.Status == 200) {
  307 + let content = result.InterfaceResult.Contents
  308 + if (content && content.length) {
  309 + if (content[0].Key) {
  310 + isVideo = true
  311 + url = content[0].Key
  312 + }
  313 + }
  314 + // for(let i=0;i<result.InterfaceResult.Contents.length;i++){
  315 + // let item = result.InterfaceResult.Contents[i]
  316 + // if(item.Key && item.Key.indexOf(body.classId) > -1){
  317 + // isVideo = true
  318 + // url = item.Key
  319 + // break
  320 + // }
  321 + // }
  322 + }
  323 + }
  324 + if (!isVideo) {
  325 + let classUrl = `https://pclive.xuedianyun.com/pcBase/pclive2/release/index.html?portalIP=saas.xuedianyun.com&portalPort=80&classId=${body.classId}&channels=2&playRecord=1#/`
  326 + res.send({code: 1, message: "文件未生成", classUrl});
  327 + } else {
  328 + let classUrl = `https://xdymp4.xuedianyun.com/${url}`
  329 + res.send({code: 0, message: "文件已生成", classUrl});
  330 + }
  331 + // 关闭obsClient
  332 + obsClient.close();
  333 + });
  334 + }else{
  335 + let classUrl = `https://pclive.xuedianyun.com/pcBase/pclive2/release/index.html?portalIP=saas.xuedianyun.com&portalPort=80&classId=${body.classId}&channels=2&playRecord=1#/`
  336 + res.send({code: 1, message: "文件未生成", classUrl});
  337 + }
  338 +})
  339 +
  340 +module.exports = router;
  1 +var express = require('express');
  2 +var router = express.Router();
  3 +const { spawn, exec } = require('child_process');
  4 +const fs = require("fs");
  5 +var path = require('path')
  6 +var ObsClient = require('esdk-obs-nodejs');
  7 +
  8 +const method = require("../config/method")
  9 +const config = require("../config/config")
  10 +
  11 +// const { GETCLASSURL, GETCLASSURLPARAMETER, PROJECTCATALOG, PROJECTWINCATALOG, BACKMEDIACONFIG } = config
  12 +const { YesterdayTime, getRequestClassIds, dayTimeYMD } = method
  13 +
  14 +let siteIds = []
  15 +
  16 +let classid = []
  17 +let classidPost = []
  18 +let parentData = {}
  19 +var classobj = {};
  20 +let className = ""
  21 +let yesterday = "" // get写入课堂的时间
  22 +
  23 +// spawn("export DISPLAY=:7", { shell: true})
  24 +
  25 +/**
  26 + *
  27 + * @param {*} id 课堂id
  28 + */
  29 +class MediaCreat {
  30 + constructor() {
  31 + }
  32 +
  33 + // 取出所有数据
  34 + async allData() {
  35 + const { startTime, endTime } = YesterdayTime()
  36 + let fileConfig = new MediaCreat().getConfigFileJson()
  37 + if (!fileConfig) return false
  38 + className = siteIds.shift()
  39 + const { GETCLASSURL, GETCLASSURLPARAMETER,classLastNumber } = JSON.parse(fileConfig)
  40 + let page = 1
  41 + if (className) {
  42 + let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, page)
  43 + //parentData[result.data.data.siteId] = new Set()
  44 + let resultList = result.data.data.list
  45 + for (let j = 0; j < resultList.length; j++) {
  46 + let item = resultList[j]
  47 + let classId = item['classId']
  48 + let number = classId.substr(classId.length - 1, 1)
  49 + if (classLastNumber.includes(number)){
  50 + classid.push(item)
  51 + //parentData[result.data.data.siteId].add(classId)
  52 + }
  53 + }
  54 + const { siteId, list, totalPage } = result.data.data
  55 + for (let i = page += 1; i <= totalPage; i++) {
  56 + let result = await getRequestClassIds(GETCLASSURL, className, GETCLASSURLPARAMETER.key, startTime, endTime, i)
  57 + let resultList = result.data.data.list
  58 + for (let j = 0; j < resultList.length; j++) {
  59 + let item = resultList[j]
  60 + let classId = item['classId']
  61 + let number = classId.substr(classId.length - 1, 1)
  62 + if (classLastNumber.includes(number)) {
  63 + classid.push(item)
  64 + // parentData[result.data.data.siteId].add(classId)
  65 + }
  66 + }
  67 + }
  68 + // parentData[result.data.data.siteId] = Array.from(parentData[result.data.data.siteId])
  69 + if (siteIds.length) {
  70 + return await new MediaCreat().allData()
  71 + }
  72 + this.wrieLog("去重前的classId:------>" + JSON.stringify(classid))
  73 + this.wrieLog("httpData:------>" + JSON.stringify(result.data))
  74 + return true
  75 + } else {
  76 + return false
  77 + }
  78 + }
  79 +
  80 + wrieLog(text) {
  81 + // 写入log
  82 + let logFile = `./log/${dayTimeYMD().ymd}.txt`
  83 + fs.appendFileSync(logFile, new Date().toLocaleString() + " " + text + '\r\n');
  84 + }
  85 +
  86 + recordingCreat(id, siteId, type) {
  87 + this.wrieLog(" 课堂录制开始:------>" + id)
  88 + let fileConfig = this.getConfigFileJson()
  89 + if (!fileConfig) return false
  90 + const { BACKMEDIACONFIG, PROJECTWINCATALOG, PROJECTCATALOG } = JSON.parse(fileConfig)
  91 + let mediaDir = PROJECTCATALOG + "/media/"
  92 + let classDir = PROJECTCATALOG + "/media/" + siteId
  93 + let ymdDir = null
  94 + if (type == 'post') {
  95 + ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + dayTimeYMD().ymd
  96 + } else {
  97 + // get 的目录创建为上一天日期
  98 + ymdDir = PROJECTCATALOG + "/media/" + siteId + "/" + yesterday
  99 + }
  100 +
  101 + if (!fs.existsSync(mediaDir)) {
  102 + fs.mkdirSync(mediaDir);
  103 + }
  104 + if (!fs.existsSync(classDir)) {
  105 + fs.mkdirSync(classDir);
  106 + }
  107 + if (!fs.existsSync(ymdDir)) {
  108 + fs.mkdirSync(ymdDir);
  109 + }
  110 + let files = fs.readdirSync(ymdDir);
  111 + // let interValGetFile = setInterval(()=>{
  112 + this.wrieLog("files:" + files)
  113 + if (files.indexOf(id + ".mp4") != -1) {
  114 + this.wrieLog("已存在:" + id + "课堂号,停止继续录制")
  115 + if (type == 'post') {
  116 + if (classidPost.length) {
  117 + let shiftData = classidPost.shift()
  118 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  119 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  120 + }
  121 + } else {
  122 + if (classid.length) {
  123 + let shiftData = classid.shift()
  124 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  125 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  126 + }
  127 + }
  128 + return
  129 + }
  130 + // 目前url是linux的写法 win系统不支持
  131 + // export DISPLAY=:7
  132 + // let url = `${path.resolve(__dirname, PROJECTWINCATALOG+"/web_capture_c")} -o=${ymdDir}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h}`
  133 + // console.log("url", url)
  134 + let url = `${PROJECTWINCATALOG}/web_capture_c -o=${ymdDir}/${id}.mp4 -u="${BACKMEDIACONFIG.url}?classId=${id}&recordMp4=${BACKMEDIACONFIG.recordMp4}&userId=${BACKMEDIACONFIG.userId}&userName=${BACKMEDIACONFIG.userName}&userRole=${BACKMEDIACONFIG.userRole}&portalIP=${BACKMEDIACONFIG.portalIP}&portalPort=${BACKMEDIACONFIG.portalPort}&channels=${BACKMEDIACONFIG.channels}&playRecord=${BACKMEDIACONFIG.playRecord}&language=zh-cn" -d=${BACKMEDIACONFIG.d} -s=${BACKMEDIACONFIG.s} -fa=${BACKMEDIACONFIG.fa} -k=${BACKMEDIACONFIG.k} -w=${BACKMEDIACONFIG.w} -h=${BACKMEDIACONFIG.h}`
  135 + exec(url, { maxBuffer: 1073741824 }, (err, stdout, stderr) => {
  136 + if (err != null) {
  137 + this.wrieLog(" 错误" + id + ":" + err)
  138 + this.wrieLog(" 错误 stdout" + id + ":" + stdout)
  139 + this.wrieLog(" 错误 stderr" + id + ":" + stderr)
  140 + return
  141 + }
  142 + let files = fs.readdirSync(ymdDir);
  143 + // let interValGetFile = setInterval(()=>{
  144 + if (files.indexOf(id + ".mp4") == -1) {
  145 + this.wrieLog(" 课堂录制未发现该" + id + "课堂号")
  146 + } else {
  147 + if (type == 'get') {
  148 + if (classid.length) {
  149 + let shiftData = classid.shift()
  150 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  151 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  152 + } else {
  153 + this.wrieLog("录制结束:------>")
  154 + fs.writeFile(ymdDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {
  155 + if (err) {
  156 + console.log(err);
  157 + }
  158 + });
  159 + }
  160 + } else {
  161 + if (classidPost.length) {
  162 + let shiftData = classidPost.shift()
  163 + this.wrieLog(" 录制下一节课 课堂号:" + shiftData['classId'])
  164 + this.recordingCreat(shiftData['classId'], shiftData['siteId'], type)
  165 + } else {
  166 + this.wrieLog("录制结束:------>")
  167 + fs.writeFile(ymdDir + "/download.json", `{ "code": "0", "success": "ok"}`, function (err) {
  168 + if (err) {
  169 + console.log(err);
  170 + }
  171 + });
  172 + }
  173 + }
  174 +
  175 + }
  176 + })
  177 + }
  178 +
  179 + getConfigFileJson() {
  180 + const buffer = fs.readFileSync(process.cwd() + "/config/config.json")
  181 + return String(buffer)
  182 + }
  183 +}
  184 +
  185 +
  186 +router.get('/recording', async function (req, res, next) {
  187 + if (classid.length > 0) {
  188 + // 有正在录制中的课堂,禁止重复
  189 + res.send({ code: "1", message: "有正在录制中的课堂", data: classid });
  190 + return
  191 + }
  192 + new MediaCreat().wrieLog("脚本录制开始:------>")
  193 + let fileConfig = new MediaCreat().getConfigFileJson()
  194 + if (!fileConfig) return false
  195 +
  196 + const { GETCLASSURLPARAMETER } = JSON.parse(fileConfig)
  197 + siteIds = GETCLASSURLPARAMETER.siteId
  198 + if (siteIds.length == 0) {
  199 + new MediaCreat().wrieLog("脚本录制,未配置siteId:------>" + siteIds)
  200 + res.send({ code: "1", message: "未配置siteId", data: siteIds });
  201 + return
  202 + }
  203 + let result = await new MediaCreat().allData()
  204 + if (result) {
  205 + // 去重
  206 + classobj = {}
  207 + classid = classid.reduce(function (item, next) {
  208 + classobj[next.classId] ? '' : classobj[next.classId] = true && item.push(next);
  209 + return item;
  210 + }, []);
  211 + // 写入log
  212 + new MediaCreat().wrieLog("去重后的classid:------>" + JSON.stringify(classid))
  213 + if (classid.length) {
  214 + for (let i = 0; i < GETCLASSURLPARAMETER.maxMedia; i++) {
  215 + let shiftData = classid.shift()
  216 + if (shiftData) {
  217 + yesterday = YesterdayTime().ymd
  218 + new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId'], 'get')
  219 + } else {
  220 + return false
  221 + }
  222 + }
  223 + res.send({ code: "0" });
  224 + } else {
  225 + res.send({ code: "1", message: "无录制数据" });
  226 + }
  227 + } else {
  228 + res.send({ code: "1", message: "无录制数据" });
  229 + }
  230 +});
  231 +
  232 +/**
  233 + * {
  234 + * "classId":[{ classId: '389675110', siteId: 'kuaikuenglish' }]
  235 + * }
  236 + */
  237 +router.post('/recording', async function (req, res, next) {
  238 + new MediaCreat().wrieLog("录制启动:------>")
  239 + let fileConfig = new MediaCreat().getConfigFileJson()
  240 + if (!fileConfig) return false
  241 + let { classId, maxMedia } = req.body
  242 + if (classidPost.length > 0) {
  243 + // 有正在录制中的课堂,禁止重复
  244 + res.send({ code: "1", message: "有正在录制中的课堂", data: classidPost });
  245 + return
  246 + }
  247 + if (classId && classId.length) {
  248 + classidPost = classId
  249 + if (!maxMedia || maxMedia == undefined) {
  250 + maxMedia = 1
  251 + }
  252 + for (let i = 0; i < maxMedia; i++) {
  253 + let shiftData = classidPost.shift()
  254 + if (shiftData) {
  255 + new MediaCreat().recordingCreat(shiftData['classId'], shiftData['siteId'], 'post')
  256 + } else {
  257 + return false
  258 + }
  259 + }
  260 + res.send({ code: "0" });
  261 + } else {
  262 + res.send({ code: "1", message: "无录制数据" });
  263 + }
  264 +
  265 +})
  266 +
  267 +// 判断该视频文件是否存在
  268 +router.post('/fileExists', async function (req, res, next) {
  269 + const body = req.body
  270 + if(!body.siteId) {
  271 + res.send({ code: 2,message:"机构编码无效" });
  272 + return
  273 + }
  274 + if(!body.classId) {
  275 + res.send({ code: 3,message:"课堂号无效" });
  276 + return
  277 + }
  278 +
  279 + // 创建ObsClient实例
  280 + var obsClient = new ObsClient({
  281 + access_key_id: 'FY27OD11ZJVC385AOTW9',
  282 + secret_access_key: 'ZqnfRjaseCtuRbE79GvqkPiYUdT83ZbB0oU7fBo3',
  283 + server : 'obs.cn-north-4.myhuaweicloud.com'
  284 + });
  285 +
  286 + let isVideo = false
  287 + let url = ""
  288 + obsClient.listObjects({
  289 + Bucket : 'xdymp4'
  290 + }, (err, result) => {
  291 + if(err){
  292 + console.error('Error-->' + err);
  293 + }else{
  294 + if(result.CommonMsg.Status == 200){
  295 + for(let i=0;i<result.InterfaceResult.Contents.length;i++){
  296 + let item = result.InterfaceResult.Contents[i]
  297 + if(item.Key && item.Key.indexOf(body.classId) > -1){
  298 + isVideo = true
  299 + url = item.Key
  300 + break
  301 + }
  302 + }
  303 + }
  304 + }
  305 + if(!isVideo){
  306 + let classUrl = `https://pclive.xuedianyun.com/pcBase/pclive2/release/index.html?portalIP=saas.xuedianyun.com&portalPort=80&classId=${body.classId}&channels=2&playRecord=1#/`
  307 + res.send({ code: 1,message:"文件未生成",classUrl });
  308 + }else{
  309 + let classUrl = `https://xdymp4.xuedianyun.com/${url}`
  310 + res.send({ code: 0,message:"文件已生成",classUrl });
  311 + }
  312 + // 关闭obsClient
  313 + obsClient.close();
  314 + });
  315 +})
  316 +
  317 +module.exports = router;