正在显示
3 个修改的文件
包含
193 行增加
和
4 行删除
| @@ -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; |
config/realTimeConfig.json
0 → 100755
| 1 | +{ | ||
| 2 | + "PROJECTWINCATALOG": "F:/project/web_capture_release/win-x64", | ||
| 3 | + "PROJECTCATALOG": "/Users/zhangaichen/Desktop/学点云/webScreen", | ||
| 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 | +} |
routes/bbgRecording.js
0 → 100644
| 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 | + if (!fs.existsSync(mediaDir)) { | ||
| 40 | + fs.mkdirSync(mediaDir); | ||
| 41 | + } | ||
| 42 | + if (!fs.existsSync(classDir)) { | ||
| 43 | + fs.mkdirSync(classDir); | ||
| 44 | + } | ||
| 45 | + if (!fs.existsSync(ymdDir)) { | ||
| 46 | + fs.mkdirSync(ymdDir); | ||
| 47 | + } | ||
| 48 | + // let files = fs.readdirSync(ymdDir); | ||
| 49 | + // let interValGetFile = setInterval(()=>{ | ||
| 50 | + // this.wrieLog("files:" + files) | ||
| 51 | + // if (files.indexOf(id + ".mp4") != -1) { | ||
| 52 | + // this.wrieLog("已存在:" + id + "课堂号,停止继续录制") | ||
| 53 | + // return | ||
| 54 | + // } | ||
| 55 | + | ||
| 56 | + // if(!classUrl){ | ||
| 57 | + // 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}"` | ||
| 58 | + // } | ||
| 59 | + // if(!duration){ | ||
| 60 | + // duration = BACKMEDIACONFIG.d | ||
| 61 | + // } | ||
| 62 | + 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}` | ||
| 63 | + this.wrieLog("全地址"+url) | ||
| 64 | + exec(url, { maxBuffer: 1073741824 }, (err, stdout, stderr) => { | ||
| 65 | + if (err != null) { | ||
| 66 | + this.wrieLog(" 错误" + id + ":" + err) | ||
| 67 | + this.wrieLog(" 错误 stdout" + id + ":" + stdout) | ||
| 68 | + this.wrieLog(" 错误 stderr" + id + ":" + stderr) | ||
| 69 | + // 删除已存元素 | ||
| 70 | + classids.splice(classids.findIndex(item => item === id),1) | ||
| 71 | + return | ||
| 72 | + } | ||
| 73 | + // let files = fs.readdirSync(ymdDir); | ||
| 74 | + // if (files.indexOf(id + ".mp4") != -1) { | ||
| 75 | + // 删除已存元素 | ||
| 76 | + classids.splice(classids.findIndex(item => item === id),1) | ||
| 77 | + this.sendMediaInfo(siteId,id,timeDir) | ||
| 78 | + // } | ||
| 79 | + }) | ||
| 80 | + } | ||
| 81 | + // 上报地址 | ||
| 82 | + async sendMediaInfo(siteId,id,timeDir){ | ||
| 83 | + // https://testmp4.obs.cn-north-4.myhuaweicloud.com/oss/media/eebbktest/20210326/1657547103.mp4 | ||
| 84 | + let MediaUrl = `https://testmp4.obs.cn-north-4.myhuaweicloud.com/oss/media/${siteId}/${timeDir}/${id}.mp4` | ||
| 85 | + let urlSizeId = { | ||
| 86 | + "eebbktest": "gdbbkwxyace", | ||
| 87 | + "gdbbk":"gdbbkwx", | ||
| 88 | + "gdbbkdev":"gdbbkwxtest" | ||
| 89 | + } | ||
| 90 | + let requestUrl = `https://${urlSizeId[siteId]}.xuedianyun.com/bbgserver/app/course/updateRecordUrl` | ||
| 91 | + let result = await axios.post( | ||
| 92 | + requestUrl, | ||
| 93 | + {meetingNumber:id,url:MediaUrl}, | ||
| 94 | + {headers: {'orgId':siteId }} | ||
| 95 | + ) | ||
| 96 | + if(result.data){ | ||
| 97 | + this.wrieLog("更新视频地址结果:"+JSON.stringify(result.data)) | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + getConfigFileJson() { | ||
| 102 | + const buffer = fs.readFileSync(process.cwd() + "/config/realTimeConfig.json") | ||
| 103 | + return String(buffer) | ||
| 104 | + } | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | +/** | ||
| 108 | + * { | ||
| 109 | + * "classId":[{ classId: '389675110', siteId: 'kuaikuenglish' }] | ||
| 110 | + * } | ||
| 111 | + */ | ||
| 112 | +router.post('/recording/:id', function (req, res, next) { | ||
| 113 | + new MediaCreat().wrieLog("录制启动:------>") | ||
| 114 | + let fileConfig = new MediaCreat().getConfigFileJson() | ||
| 115 | + if (!fileConfig) return false | ||
| 116 | + /** | ||
| 117 | + * classId:课堂号 | ||
| 118 | + * siteId:机构编码 | ||
| 119 | + * url:课堂地址 | ||
| 120 | + * duration:录制时长 | ||
| 121 | + * width height 录制视频宽高 | ||
| 122 | + * frameRate 帧率 | ||
| 123 | + * bitrate 码率 | ||
| 124 | + */ | ||
| 125 | + let { classId,siteId,url:classUrl,duration,width,height,frameRate,bitrate } = req.body | ||
| 126 | + | ||
| 127 | + if (classId && siteId && classUrl && duration && width && height && frameRate && bitrate) { | ||
| 128 | + if(!Number.isInteger(classId) || !Number.isInteger(duration) || !Number.isInteger(width) || !Number.isInteger(height) || !Number.isInteger(frameRate) || !Number.isInteger(bitrate)){ | ||
| 129 | + res.send({ code: "3", message: "参数类型错误" }); | ||
| 130 | + return | ||
| 131 | + } | ||
| 132 | + let maxTime = 10*60*60*1000 | ||
| 133 | + if(duration < 0 || duration > maxTime){ | ||
| 134 | + res.send({ code: "4", message: "参数范围错误" }); | ||
| 135 | + return | ||
| 136 | + } | ||
| 137 | + // 避免多次重复课堂号请求 | ||
| 138 | + if(!classids.includes(classId)){ | ||
| 139 | + // 没有正在录制的该课堂 | ||
| 140 | + classids.push(classId) | ||
| 141 | + bitrate = bitrate * 1024 // 转换 * 1024 | ||
| 142 | + new MediaCreat().recordingCreat(classId, siteId,classUrl,duration,width,height,frameRate,bitrate) | ||
| 143 | + res.send({ code: "0" }); | ||
| 144 | + }else{ | ||
| 145 | + res.send({ code: "2", message: "重复请求课堂号" }); | ||
| 146 | + } | ||
| 147 | + } else { | ||
| 148 | + res.send({ code: "1", message: "缺少参数" }); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | +}) | ||
| 152 | + | ||
| 153 | + | ||
| 154 | +module.exports = router; |
-
请 注册 或 登录 后发表评论