master
DIYgod 6 years ago
parent e796e2f712
commit b3c5ed21ec
No known key found for this signature in database
GPG Key ID: EC0B76A252D3EF67
  1. 58
      .eslintrc
  2. 3
      .gitignore
  3. 31
      README.md
  4. 7
      blacklist
  5. 15
      config.js
  6. 10
      docker-compose.yml
  7. 52
      index.js
  8. 21
      middleware/access-control.js
  9. 14
      middleware/header.js
  10. 14
      middleware/onerror.js
  11. 16
      models/danmaku.js
  12. 16
      package.json
  13. 7
      router.js
  14. 23
      routes/all.js
  15. 134
      routes/bilibili.js
  16. 63
      routes/get.js
  17. 17
      routes/list.js
  18. 143
      routes/post.js
  19. 124
      routes/v2/bilibili.js
  20. 63
      routes/v2/get.js
  21. 117
      routes/v2/post.js
  22. 72
      routes/video-bilibili.js
  23. 11
      tools/blank.js
  24. 19
      tools/logger.js
  25. 13
      tools/mongodb.js
  26. 26
      tools/redis.js
  27. 29
      utils/logger.js
  28. 30
      utils/mongodb.js
  29. 35
      utils/redis.js
  30. 2022
      yarn.lock

@ -0,0 +1,58 @@
{
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"plugins": ["prettier"],
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module",
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
},
"env": {
"node": true,
"es6": true
},
"rules": {
"no-console": 0,
"block-scoped-var": 1,
"curly": 1,
"eqeqeq": 1,
"no-global-assign": 1,
"no-implicit-globals": 1,
"no-labels": 1,
"no-multi-str": 1,
"comma-spacing": 1,
"comma-style": 1,
"func-call-spacing": 1,
"keyword-spacing": 1,
"linebreak-style": 1,
"lines-around-comment": 1,
"no-multiple-empty-lines": 1,
"space-infix-ops": 1,
"arrow-spacing": 1,
"no-var": 1,
"prefer-const": 1,
"no-unsafe-negation": 1,
"array-callback-return": 1,
"dot-notation": 1,
"no-eval": 1,
"no-extend-native": 1,
"no-extra-label": 1,
"semi": 1,
"space-before-blocks": 1,
"space-in-parens": 1,
"space-unary-ops": 1,
"spaced-comment": 1,
"arrow-body-style": 1,
"arrow-parens": 1,
"no-restricted-imports": 1,
"no-duplicate-imports": 1,
"no-useless-computed-key": 1,
"no-useless-rename": 1,
"rest-spread-spacing": 1,
"no-trailing-spaces": 1,
"quotes": [1, "single"],
"no-control-regex": 0,
"prettier/prettier": 0
}
}

3
.gitignore vendored

@ -1,2 +1,3 @@
node_modules
logs/DPlayer.log*
error.log
combined.log

@ -1,4 +1,4 @@
# DPlayer backend
# DPlayer node
> Node.js backend for [DPlayer](https://github.com/DIYgod/DPlayer)
@ -42,30 +42,9 @@ docker-compose stop
[Telegram Group](https://t.me/adplayer)
[QQ Group: 415835947](https://shang.qq.com/wpa/qunwpa?idkey=bf22213ae0028a82e5adf3f286dfd4f01e0997dc9f1dcd8e831a0a85e799be17)
## Author
## Related Projects
**DPlayer-node** © [DIYgod](https://github.com/DIYgod), Released under the [MIT](./LICENSE) License.<br>
Authored and maintained by DIYgod with help from contributors ([list](https://github.com/MoePlayer/DPlayer-node/contributors)).
- [DPlayer](https://github.com/DIYgod/DPlayer)
- [DPlayer-data(weekly backup for api.prprpr.me/dplayer)](https://github.com/DIYgod/DPlayer-data)
- [DPlayer-for-typecho](https://github.com/volio/DPlayer-for-typecho)
- [Hexo-tag-dplayer](https://github.com/NextMoe/hexo-tag-dplayer)
- [DPlayer_for_Z-BlogPHP](https://github.com/fghrsh/DPlayer_for_Z-BlogPHP)
- [纸飞机视频区插件(DPlayer for Discuz!)](https://coding.net/u/Click_04/p/video/git)
- [dplayer_py_backend](https://github.com/dixyes/dplayer_py_backend)
- [dplayer_lua_backend](https://github.com/dixyes/dplayer_lua_backend)
- [DPlayer for WordPress](https://github.com/BlueCocoa/DPlayer-WordPress)
- [Vue-DPlayer](https://github.com/sinchang/vue-dplayer)
## LICENSE
[The Star And Thank Author License (SATA)](https://github.com/DIYgod/DPlayer/blob/master/LICENSE)
> Blog [@DIYgod](https://diygod.me) · GitHub [@DIYgod](https://github.com/DIYgod) · Twitter [@DIYgod](https://twitter.com/DIYgod) · Telegram Channel [@awesomeDIYgod](https://t.me/awesomeDIYgod)

@ -1,7 +0,0 @@
Can be username and IP and referer
username
0.0.0.0
175.180.108.110
219.133.201.230
llj22.com
190fl.cc

@ -0,0 +1,15 @@
module.exports = {
port: process.env.PORT || 1207,
mongodb: {
username: process.env.MONGO_USERNAME || null,
password: process.env.MONGO_PASSWORD || null,
host: process.env.MONGO_HOST || '127.0.0.1',
port: process.env.MONGO_PORT || '27017',
database: process.env.MONGO_DATABASE || 'danmaku',
},
redis: {
host: process.env.REDIS_HOST || '127.0.0.1',
port: process.env.REDIS_PORT || '6379',
password: process.env.REDIS_PASSWORD || null,
}
};

@ -22,11 +22,11 @@ services:
ports:
- 1207:1207
environment:
REDIS_PORT_6379_TCP_ADDR: "redis"
REDIS_PORT_6379_TCP_PORT: 6379
MONGO_PORT_27017_TCP_ADDR: "mongo"
MONGO_PORT_27017_TCP_PORT: 27017
MONGO_INSTANCE_NAME: "danmaku"
REDIS_HOST: "redis"
REDIS_PORT: 6379
MONGO_HOST: "mongo"
MONGO_PORT: 27017
MONGO_DATABASE: "danmaku"
volumes:
- ~/dplayer/logs:/usr/src/app/logs
- ~/dplayer/pm2logs:/root/.pm2/logs

@ -1,18 +1,34 @@
var express = require('express');
var logger = require('./tools/logger');
require('./tools/mongodb');
logger.info(`🍻 DPlayer start! Cheers!`);
var app = express();
app.all('*', require('./routes/all'));
app.get('/', require('./routes/get'));
app.post('/', require('./routes/post'));
// app.get('/list', require('./routes/list'));
app.get('/bilibili', require('./routes/bilibili'));
// app.get('/video/bilibili', require('./routes/video-bilibili'));
app.get('/v2', require('./routes/v2/get'));
app.post('/v2', require('./routes/v2/post'));
app.get('/v2/bilibili', require('./routes/v2/bilibili'));
app.listen(1207);
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const logger = require('./utils/logger');
const config = require('./config');
const mongodb = require('./utils/mongodb');
const redis = require('./utils/redis');
const onerror = require('./middleware/onerror');
const header = require('./middleware/header.js');
const accessControl = require('./middleware/access-control.js');
const router = require('./router');
process.on('uncaughtException', (e) => {
logger.error('uncaughtException: ' + e);
});
logger.info('🎉 DPlayer start! Cheers!');
const app = new Koa();
app.proxy = true;
app.context.mongodb = mongodb;
app.context.redis = redis;
app.use(bodyParser());
app.use(onerror);
app.use(header);
app.use(accessControl);
app.use(router.routes()).use(router.allowedMethods());
app.listen(config.port);
logger.info('Listening Port ' + config.port);

@ -0,0 +1,21 @@
const blacklist = (process.env.BLACKLIST && process.env.BLACKLIST.split(',')) || [];
const whitelist = process.env.WHITELIST && process.env.WHITELIST.split(',');
module.exports = async (ctx, next) => {
const ip = ctx.ips[0] || ctx.ip;
const referer = ctx.request.headers.referer;
const refererAllowed = (whitelist && whitelist.indexOf(referer) !== -1) || blacklist.indexOf(referer) === -1;
const ipAllowed = (whitelist && whitelist.indexOf(ip) !== -1) || blacklist.indexOf(ip) === -1;
if (refererAllowed && ipAllowed) {
await next();
} else {
ctx.response.status = 403;
ctx.body = JSON.stringify({
code: 1,
msg: `${!refererAllowed ? '该站点' : '你的 IP '}没有访问权限`,
});
}
};

@ -0,0 +1,14 @@
const logger = require('../utils/logger');
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild',
'Access-Control-Allow-Methods': 'PUT, POST, GET, DELETE, OPTIONS',
'Content-Type': 'application/json; charset=UTF-8',
'Cache-Control': 'no-cache',
};
module.exports = async (ctx, next) => {
logger.info(`${ctx.url}, user IP: ${ctx.ips[0] || ctx.ip}`);
ctx.set(headers);
await next();
};

@ -0,0 +1,14 @@
const logger = require('../utils/logger');
module.exports = async (ctx, next) => {
try {
await next();
} catch (err) {
logger.error('Promise error: ' + (err instanceof Error ? err.stack : err));
ctx.set({
'Content-Type': 'text/html; charset=UTF-8',
});
ctx.body = `DPlayer-node 发生了一些意外: <pre>${err instanceof Error ? err.stack : err}</pre>`;
ctx.status = 500;
}
};

@ -1,16 +0,0 @@
var mongoose = require('../tools/mongodb');
var danmakuSchema = new mongoose.Schema({
player: {
type: [String], index: true
},
author: String,
time: Number,
text: String,
color: String,
type: String,
ip: String,
referer: String
});
var danmaku = mongoose.model('dan', danmakuSchema);
module.exports = danmaku;

@ -5,14 +5,18 @@
"main": "index.js",
"author": "DIYgod",
"license": "MIT",
"devDependencies": {},
"devDependencies": {
"eslint": "5.0.1",
"eslint-config-prettier": "2.9.0",
"eslint-plugin-prettier": "2.6.1"
},
"dependencies": {
"blueimp-md5": "2.10.0",
"express": "4.16.2",
"log4js": "2.4.1",
"mongoose": "4.13.9",
"node-fetch": "1.7.3",
"koa": "2.5.1",
"koa-bodyparser": "4.2.1",
"koa-router": "7.4.0",
"mongoose": "5.1.7",
"redis": "2.8.0",
"winston": "3.0.0",
"xml2js": "0.4.19"
}
}

@ -0,0 +1,7 @@
const Router = require('koa-router');
const router = new Router();
router.get('/', require('./routes/get'));
router.post('/', require('./routes/post'));
module.exports = router;

@ -1,23 +0,0 @@
var logger = require('../tools/logger');
var blank = require('../tools/blank');
module.exports = function (req, res, next) {
if (req.headers.referer && blank(req.headers.referer)) {
logger.info(`Reject all form ${req.headers.referer} for black referer.`);
res.send(`{"code": 6, "msg": "black referer"}`);
return;
}
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
res.header('Cache-control', 'no-cache');
if (req.method == 'OPTIONS') {
res.send(200);
}
else {
next();
}
};

@ -1,134 +0,0 @@
var url = require('url');
var logger = require('../tools/logger');
var redis = require('../tools/redis');
var fetch = require('node-fetch');
var parseString = require('xml2js').parseString;
module.exports = function (req, res) {
res.header('content-type', 'application/json; charset=utf-8');
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
var query = url.parse(req.url,true).query;
var aid = query.aid;
var cid = query.cid;
function addZero(str, length){
return new Array(Math.max(length - str.length + 1, 0)).join("0") + str;
}
if (cid) {
redis.client.get(`bilibilicid2dan${cid}`, function(err, reply) {
if (reply) {
logger.info(`Bilibili cid2dan ${cid} form redis, IP: ${ip}`);
res.send(reply);
}
else {
logger.info(`Bilibili cid2dan ${cid} form origin, IP: ${ip}`);
var dan = {
code: 1,
danmaku: []
};
fetch(`http://comment.bilibili.com/${cid}.xml`).then(
response => response.text()
).then((data) => {
parseString(data, function (err, result) {
var danOriginal = result.i.d;
for (var i = 0; i < danOriginal.length; i++) {
var info = danOriginal[i].$.p.split(',');
var type = '';
if (info[1] === '4') {
type = 'bottom';
}
else if (info[1] === '5') {
type = 'top';
}
else {
type = 'right';
}
var danOne = {
author: 'bilibili' + info[6],
time: info[0],
text: danOriginal[i]._,
color: '#' + addZero(parseInt(info[3]).toString(16), 6),
type: type
};
dan.danmaku.push(danOne);
}
var sendDan = JSON.stringify(dan);
res.send(sendDan);
redis.set(`bilibilicid2dan${cid}`, sendDan);
});
}
).catch(
e => logger.error("Bilibilib Error: getting danmaku", e)
);
}
});
}
else {
redis.client.get(`bilibiliaid2dan${aid}`, function(err, reply) {
if (reply) {
logger.info(`Bilibili aid2dan ${aid} form redis, IP: ${ip}`);
res.send(reply);
}
else {
logger.info(`Bilibili aid2dan ${aid} form origin, IP: ${ip}`);
var dan = {
code: 1,
danmaku: []
};
fetch(`http://www.bilibili.com/widget/getPageList?aid=${aid}`).then(
response => response.json()
).then((data) => {
fetch(`http://comment.bilibili.com/${data[0].cid}.xml`).then(
response => response.text()
).then((data) => {
parseString(data, function (err, result) {
var danOriginal = result.i.d;
for (var i = 0; i < danOriginal.length; i++) {
var info = danOriginal[i].$.p.split(',');
var type = '';
if (info[1] === '4') {
type = 'bottom';
}
else if (info[1] === '5') {
type = 'top';
}
else {
type = 'right';
}
var danOne = {
author: 'bilibili' + info[6],
time: info[0],
text: danOriginal[i]._,
color: '#' + addZero(parseInt(info[3]).toString(16), 6),
type: type
};
dan.danmaku.push(danOne);
}
var sendDan = JSON.stringify(dan);
res.send(sendDan);
redis.set(`bilibiliaid2dan${aid}`, sendDan);
});
}
).catch(
e => logger.error("Bilibilib Error: getting danmaku", e)
);
}
).catch(
e => logger.error("Bilibilib Error: getting cid", e)
);
}
});
}
};

@ -1,43 +1,32 @@
var url = require('url');
var logger = require('../tools/logger');
var danmaku = require('../models/danmaku');
var redis = require('../tools/redis');
function htmlEncode (str) {
return str.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;')
.replace(/\//g, '&#x2f;');
}
module.exports = function (req, res) {
res.header('content-type', 'application/json; charset=utf-8');
module.exports = async (ctx) => {
const { id, limit } = ctx.request.query;
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
var query = url.parse(req.url,true).query;
var id = query.id;
var max = query.max;
redis.client.get(`dplayer${id}`, function(err, reply) {
if (reply) {
logger.info(`DPlayer id ${id} form redis, IP: ${ip}`);
res.send(reply);
let data = await ctx.redis.get(`danmaku${id}`);
if (data) {
data = JSON.parse(data);
if (limit) {
data = data.slice(-1 * parseInt(limit));
}
else {
logger.info(`DPlayer id ${id} form mongodb, IP: ${ip}`);
danmaku.find({player: id}, function (err, data) {
if (err) {
logger.error(err);
}
var dan = {
code: 1,
danmaku: []
};
dan.danmaku = max ? data.slice(0, max) : data;
var sendDan = JSON.stringify(dan);
res.send(sendDan);
redis.set(`dplayer${id}`, sendDan);
})
ctx.response.set('X-Koa-Redis', 'true');
} else {
data = await ctx.mongodb.find({ id }) || [];
ctx.redis.set(`danmaku${id}`, JSON.stringify(data));
if (limit) {
data = data.slice(-1 * parseInt(limit));
}
ctx.response.set('X-Koa-Mongodb', 'true');
}
ctx.body = JSON.stringify({
code: 0,
data: data.map((item) => [item.time || 0, item.type || 0, item.color || 16777215, htmlEncode(item.author) || 'DPlayer', htmlEncode(item.text) || '']),
});
};

@ -1,17 +0,0 @@
var url = require('url');
var logger = require('../tools/logger');
var danmaku = require('../models/danmaku');
module.exports = function (req, res) {
danmaku.distinct('player', function (err, data) {
if (err) {
logger.error(err);
}
var json = ``;
for (var i = 0; i < data.length; i++) {
json += data[i] + `<br>`;
}
res.send(json);
})
};

@ -1,117 +1,32 @@
var logger = require('../tools/logger');
var danmaku = require('../models/danmaku');
var redis = require('../tools/redis');
var blank = require('../tools/blank');
function htmlEncode(str) {
return str.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#x27;")
.replace(/\//g, "&#x2f;");
}
var postIP = [];
module.exports = function (req, res) {
var body = '';
var jsonStr = {};
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
// check black ip
if (blank(ip)) {
logger.info(`Reject POST form ${ip} for black ip.`);
res.send(`{"code": -1, "msg": "Rejected for black ip."}`);
return;
}
// frequency limitation
if (postIP.indexOf(ip) !== -1) {
logger.info(`Reject POST form ${ip} for frequent operation.`);
res.send(`{"code": -2, "msg": "Rejected for frequent operation."}`);
return;
}
else {
postIP.push(ip);
setTimeout(function () {
postIP.splice(0, 1);
}, 1000);
}
req.on('data', dataListener);
req.on('end', endListener);
function dataListener (chunk) {
body += chunk;
}
function endListener () {
cleanListener();
try {
jsonStr = JSON.parse(body);
} catch (err) {
jsonStr = {};
}
// check data
if (jsonStr.player === undefined
|| jsonStr.author === undefined
|| jsonStr.time === undefined
|| jsonStr.text === undefined
|| jsonStr.color === undefined
|| jsonStr.type === undefined
|| jsonStr.text.length >= 30) {
logger.info(`Reject POST form ${ip} for illegal data: ${JSON.stringify(jsonStr)}`);
res.send(`{"code": -3, "msg": "Rejected for illegal data"}`);
return;
}
// check token: set it yourself
function checkToken (token) {
return true;
}
if (!checkToken(jsonStr.token)) {
logger.info(`Rejected POST form ${ip} for illegal token: ${jsonStr.token}`);
res.send(`{"code": -4, "msg": "Rejected for illegal token: ${jsonStr.token}"}`);
return;
const logger = require('../utils/logger');
module.exports = async (ctx) => {
const body = ctx.request.body;
const dan = new ctx.mongodb({
id: body.id,
author: body.author,
time: body.time,
text: body.text,
color: body.color,
type: body.type,
ip: ctx.ips[0] || ctx.ip,
referer: ctx.headers.referer
});
dan.save((err, data) => {
if (err) {
logger.error(err);
ctx.body = JSON.stringify({
code: 1,
msg: 'Database error',
});
}
// check black username
if (blank(jsonStr.author)) {
logger.info(`Reject POST form ${jsonStr.author} for black user.`);
res.send(`{"code": -5, "msg": "Rejected for black user."}`);
return;
else {
ctx.body = JSON.stringify({
code: 0,
data,
});
ctx.redis.del(`danmaku${data.id}`);
}
logger.info(`POST form ${ip}, data: ${JSON.stringify(jsonStr)}`);
var dan = new danmaku({
player: htmlEncode(jsonStr.player),
author: htmlEncode(jsonStr.author),
time: jsonStr.time,
text: htmlEncode(jsonStr.text),
color: htmlEncode(jsonStr.color),
type: htmlEncode(jsonStr.type),
ip: ip,
referer: req.headers.referer
});
dan.save(function (err, d) {
if (err) {
logger.error(err);
res.send(`{"code": 0, "msg": "Error happens, please contact system administrator."}`);
}
else {
res.send(`{"code": 1, "data": ${JSON.stringify(d)}}`);
redis.client.del(`dplayer${htmlEncode(jsonStr.player)}`);
}
});
}
function cleanListener () {
req.removeListener('data', dataListener);
req.removeListener('end', endListener);
}
});
};

@ -1,124 +0,0 @@
var url = require('url');
var logger = require('../../tools/logger');
var redis = require('../../tools/redis');
var fetch = require('node-fetch');
var parseString = require('xml2js').parseString;
module.exports = function (req, res) {
res.header('content-type', 'application/json; charset=utf-8');
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
var query = url.parse(req.url, true).query;
var aid = query.aid;
var cid = query.cid;
function addZero (str, length) {
return new Array(Math.max(length - str.length + 1, 0)).join("0") + str;
}
if (cid) {
redis.client.get(`v2bilibilicid2dan${cid}`, function (err, reply) {
if (reply) {
logger.info(`v2: Bilibili cid2dan ${cid} form redis, IP: ${ip}`);
res.send(reply);
}
else {
logger.info(`v2: Bilibili cid2dan ${cid} form origin, IP: ${ip}`);
var dan = {
code: 0,
version: 2,
danmaku: []
};
fetch(`http://comment.bilibili.com/${cid}.xml`).then(
response => response.text()
).then((data) => {
parseString(data, function (err, result) {
var danOriginal = result.i.d;
for (var i = 0; i < danOriginal.length; i++) {
var info = danOriginal[i].$.p.split(',');
var type = '';
if (info[1] === '4') {
type = 2;
}
else if (info[1] === '5') {
type = 1;
}
else {
type = 0;
}
var danOne = [parseInt(info[0]), type, '#' + addZero(parseInt(info[3]).toString(16), 6), 'bilibili' + info[6], danOriginal[i]._];
dan.danmaku.push(danOne);
}
var sendDan = JSON.stringify(dan);
res.send(sendDan);
redis.set(`v2bilibilicid2dan${cid}`, sendDan);
});
}
).catch(
e => logger.error("Bilibilib Error: getting danmaku", e)
);
}
});
}
else {
redis.client.get(`v2bilibiliaid2dan${aid}`, function (err, reply) {
if (reply) {
logger.info(`v2: Bilibili aid2dan ${aid} form redis, IP: ${ip}`);
res.send(reply);
}
else {
logger.info(`v2: Bilibili aid2dan ${aid} form origin, IP: ${ip}`);
var dan = {
code: 0,
version: 2,
danmaku: []
};
fetch(`http://www.bilibili.com/widget/getPageList?aid=${aid}`).then(
response => response.json()
).then((data) => {
fetch(`http://comment.bilibili.com/${data[0].cid}.xml`).then(
response => response.text()
).then((data) => {
parseString(data, function (err, result) {
var danOriginal = result.i.d;
for (var i = 0; i < danOriginal.length; i++) {
var info = danOriginal[i].$.p.split(',');
var type = '';
if (info[1] === '4') {
type = 2;
}
else if (info[1] === '5') {
type = 1;
}
else {
type = 0;
}
var danOne = [parseInt(info[0]), type, '#' + addZero(parseInt(info[3]).toString(16), 6), 'bilibili' + info[6], danOriginal[i]._];
dan.danmaku.push(danOne);
}
var sendDan = JSON.stringify(dan);
res.send(sendDan);
redis.set(`v2bilibiliaid2dan${aid}`, sendDan);
});
}
).catch(
e => logger.error("Bilibilib Error: getting danmaku", e)
);
}
).catch(
e => logger.error("Bilibilib Error: getting cid", e)
);
}
});
}
};

@ -1,63 +0,0 @@
var url = require('url');
var logger = require('../../tools/logger');
var danmaku = require('../../models/danmaku');
var redis = require('../../tools/redis');
module.exports = function (req, res) {
res.header('content-type', 'application/json; charset=utf-8');
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
var query = url.parse(req.url, true).query;
var id = query.id;
var max = query.max;
redis.client.get(`v2get${id}`, function (err, reply) {
if (reply) {
logger.info(`v2: DPlayer id ${id} form redis, IP: ${ip}`);
var data = JSON.parse(reply);
var data = max ? data.slice(data.length - max, data.length) : data;
var typeMap = {
'right': 0,
'top': 1,
'bottom': 2
}
res.send(JSON.stringify({
code: 0,
version: 2,
danmaku: data.map(item => [item.time, typeMap[item.type], item.color, item.author, item.text])
}));
}
else {
logger.info(`v2: DPlayer id ${id} form mongodb, IP: ${ip}`);
danmaku.find({ player: id }, function (err, data) {
if (err) {
logger.error(err);
}
redis.set(`v2get${id}`, JSON.stringify(data));
var data = max ? data.slice(data.length - max, data.length) : data;
var typeMap = {
'right': 0,
'top': 1,
'bottom': 2
}
res.send(JSON.stringify({
code: 0,
version: 2,
danmaku: data.map(item => [item.time, typeMap[item.type], item.color, item.author, item.text])
}));
})
}
});
};

@ -1,117 +0,0 @@
var logger = require('../../tools/logger');
var danmaku = require('../../models/danmaku');
var redis = require('../../tools/redis');
var blank = require('../../tools/blank');
function htmlEncode (str) {
return str.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#x27;")
.replace(/\//g, "&#x2f;");
}
var postIP = [];
module.exports = function (req, res) {
var body = '';
var jsonStr = {};
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
// check black ip
if (blank(ip)) {
logger.info(`v2: Reject POST form ${ip} for black ip.`);
res.send(`{"code": 1, "msg": "black ip"}`);
return;
}
// frequency limitation
if (postIP.indexOf(ip) !== -1) {
logger.info(`v2: Reject POST form ${ip} for frequent operation.`);
res.send(`{"code": 2, "msg": "frequent operation"}`);
return;
}
else {
postIP.push(ip);
setTimeout(function () {
postIP.splice(0, 1);
}, 5000);
}
req.on('data', dataListener);
req.on('end', endListener);
function dataListener (chunk) {
body += chunk;
}
function endListener () {
cleanListener();
try {
jsonStr = JSON.parse(body);
} catch (err) {
jsonStr = {};
}
// check data
if (jsonStr.player === undefined
|| jsonStr.author === undefined
|| jsonStr.time === undefined
|| jsonStr.text === undefined
|| jsonStr.color === undefined
|| jsonStr.type === undefined
|| jsonStr.text.length >= 30) {
logger.info(`v2: Reject POST form ${ip} for illegal data: ${JSON.stringify(jsonStr)}`);
res.send(`{"code": 3, "msg": "illegal data"}`);
return;
}
// check token: set it yourself
function checkToken (token) {
return true;
}
if (!checkToken(jsonStr.token)) {
logger.info(`v2: Rejected POST form ${ip} for illegal token: ${jsonStr.token}`);
res.send(`{"code": 4, "msg": "illegal token: ${jsonStr.token}"}`);
return;
}
// check black username
if (blank(jsonStr.author)) {
logger.info(`v2: Reject POST form ${jsonStr.author} for black user.`);
res.send(`{"code": 5, "msg": "black user"}`);
return;
}
logger.info(`v2: POST form ${ip}, data: ${JSON.stringify(jsonStr)}`);
var dan = new danmaku({
player: htmlEncode(jsonStr.player),
author: htmlEncode(jsonStr.author),
time: jsonStr.time,
text: htmlEncode(jsonStr.text),
color: htmlEncode(jsonStr.color),
type: htmlEncode(jsonStr.type),
ip: ip,
referer: req.headers.referer
});
dan.save(function (err, d) {
if (err) {
logger.error(err);
res.send(`{"code": -1, "msg": "Database error"}`);
}
else {
res.send(`{"code": 0, "data": ${JSON.stringify(d)}}`);
redis.client.del(`v2get${htmlEncode(jsonStr.player)}`);
}
});
}
function cleanListener () {
req.removeListener('data', dataListener);
req.removeListener('end', endListener);
}
};

@ -1,72 +0,0 @@
var url = require('url');
var logger = require('../tools/logger');
var redis = require('../tools/redis');
var fetch = require('node-fetch');
var md5 = require('blueimp-md5');
var xml2js = require('xml2js');
var parseString = xml2js.parseString;
var appkey = 'f3bb208b3d081dc8';
var secret = '1c15888dc316e05a15fdd0a02ed6584f';
function getData(cid, res, type) {
var para = `cid=${cid}&from=miniplay&player=1&quality=2&type=mp4`;
var sign = md5(`${para}${secret}`);
var api = `http://interface.bilibili.com/playurl?${para}&sign=${sign}`;
if (type === '1') {
res.send(api);
}
else {
fetch(api, {
headers: {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36'},
}).then(
response => response.text()
).then((data) => {
parseString(data, { explicitArray: false }, function (err, result) {
// res.send(result.video.durl.url.replace('http://', 'https://'));
res.redirect(301, result.video.durl.url.replace('http://', 'https://'));
});
}
).catch(
e => logger.error("Bilibilib Error: getting data", e)
);
}
}
module.exports = function (req, res) {
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
var query = url.parse(req.url,true).query;
var aid = query.aid;
var cid = query.cid;
var type = query.type;
if (cid) {
logger.info(`Bilibili cid2video ${cid}, IP: ${ip}`);
getData(cid, res, type);
}
else {
redis.client.get(`bilibiliaid2cid${aid}`, function(err, reply) {
if (reply) {
logger.info(`Bilibili aid2video ${aid} form redis, IP: ${ip}`);
getData(reply, res, type);
}
else {
logger.info(`Bilibili aid2video ${aid} form origin, IP: ${ip}`);
fetch(`http://www.bilibili.com/widget/getPageList?aid=${aid}`).then(
response => response.json()
).then((data) => {
redis.set(`bilibiliaid2cid${aid}`, data[0].cid);
getData(data[0].cid, res, type);
}
).catch(
e => logger.error("Bilibili aid2video Error: getting cid", e)
);
}
});
}
};

@ -1,11 +0,0 @@
var fs = require('fs');
var blanklist = fs.readFileSync('blacklist').toString().split('\n');
module.exports = function (text) {
for (var i = 0; i < blanklist.length; i++) {
if (new RegExp(blanklist[i]).test(text)) {
return true;
}
}
return false;
}

@ -1,19 +0,0 @@
var log4js = require('log4js');
log4js.configure({
appenders: {
DPlayer: {
type: 'file',
filename: 'logs/DPlayer.log',
maxLogSize: 20480,
backups: 3,
compress: true
},
console: {
type: 'console'
}
},
categories: { default: { appenders: ['DPlayer', 'console'], level: 'INFO' } }
});
var logger = log4js.getLogger('DPlayer');
module.exports = logger;

@ -1,13 +0,0 @@
var mongoose = require('mongoose');
var mongodbUrl;
if (process.env.MONGO_PORT_27017_TCP_ADDR && process.env.MONGO_PORT_27017_TCP_PORT && process.env.MONGO_INSTANCE_NAME) {
mongodbUrl = 'mongodb://' + process.env.MONGO_PORT_27017_TCP_ADDR + ':' + process.env.MONGO_PORT_27017_TCP_PORT + '/' + process.env.MONGO_INSTANCE_NAME;
}
else {
mongodbUrl = 'mongodb://127.0.0.1:27017/danmaku';
}
mongoose.connect(mongodbUrl, {
useMongoClient: true,
});
module.exports = mongoose;

@ -1,26 +0,0 @@
var logger = require('./logger');
var redis = require("redis");
var client;
if (process.env.REDIS_PORT_6379_TCP_ADDR && process.env.REDIS_PORT_6379_TCP_PORT) {
client = redis.createClient({
host: process.env.REDIS_PORT_6379_TCP_ADDR,
port: process.env.REDIS_PORT_6379_TCP_PORT
});
}
else {
client = redis.createClient();
}
client.on("error", function (err) {
logger.error('Redis Error ' + err);
});
module.exports = {
set: function (key, value) {
client.set(key, value, redis.print);
client.expire(key, 86400);
logger.info('Set redis: ' + key);
},
client: client
};

@ -0,0 +1,29 @@
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
//
// - Write to all logs with level `info` and below to `combined.log`
// - Write all logs error (and below) to `error.log`.
//
new winston.transports.File({
filename: 'logs/error.log',
level: 'error',
}),
new winston.transports.File({ filename: 'logs/combined.log' }),
],
});
//
// If we're not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
logger.add(
new winston.transports.Console({
format: winston.format.combine(winston.format.colorize(), winston.format.simple()),
})
);
module.exports = logger;

@ -0,0 +1,30 @@
const mongoose = require('mongoose');
const config = require('../config');
const logger = require('./logger');
mongoose.connect(`mongodb://${(config.mongodb.username && config.mongodb.password) ? `${config.mongodb.username}:${config.mongodb.password}@` : ''}${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.database}`);
const db = mongoose.connection;
db.on('error', (e) => {
logger.error('Mongodb error: ', e);
});
db.once('open', () => {
logger.info('Mongodb connected');
});
const danmakuSchema = new mongoose.Schema({
id: {
type: String,
index: true,
},
author: String,
time: Number,
text: String,
color: Number,
type: Number,
ip: String,
referer: String,
});
const danmaku = mongoose.model('dan', danmakuSchema);
module.exports = danmaku;

@ -0,0 +1,35 @@
const logger = require('./logger');
const config = require('../config');
const redis = require('redis');
const { promisify } = require('util');
const options = {
host: config.redis.host,
port: config.redis.port,
password: config.redis.password,
};
if (!options.password) {
delete options.password;
}
const client = redis.createClient(options);
client.on('error', (e) => {
logger.error('Redis error: ', e);
});
client.on('connect', () => {
logger.info('Redis connected');
});
const getAsync = promisify(client.get).bind(client);
module.exports = {
set: (key, value) => {
logger.info('Set redis: ' + key);
client.set(key, value);
},
get: async (key) => await getAsync(key),
del: (key) => {
client.del(key);
},
};

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save