first commit

master
DIYgod 7 years ago
commit ce24a65ca1
No known key found for this signature in database
GPG Key ID: EC0B76A252D3EF67
  1. 2
      .gitignore
  2. 5
      Dockerfile
  3. 43
      README.md
  4. 3
      blacklist
  5. 32
      docker-compose.yml
  6. 14
      index.js
  7. 0
      logs/.gitkeep
  8. 16
      models/danmaku.js
  9. 18
      package.json
  10. 17
      routes/all.js
  11. 134
      routes/bilibili.js
  12. 43
      routes/get.js
  13. 17
      routes/list.js
  14. 118
      routes/post.js
  15. 72
      routes/video-bilibili.js
  16. 20
      tools/logger.js
  17. 11
      tools/mongodb.js
  18. 26
      tools/redis.js

2
.gitignore vendored

@ -0,0 +1,2 @@
node_modules
logs/DPlayer.log*

@ -0,0 +1,5 @@
FROM node:6-onbuild
WORKDIR /usr/src/app
RUN npm install pm2 -g
EXPOSE 1207
CMD pm2-docker index.js

@ -0,0 +1,43 @@
# DPlayer backend
> Node.js backend for [DPlayer](https://github.com/DIYgod/DPlayer)
## Usage
```
$ docker-compose build
$ docker-compose pull
$ docker-compose up
```
## Communication Groups
[Telegram Group](https://t.me/adplayer)
[QQ Group: 415835947](https://shang.qq.com/wpa/qunwpa?idkey=bf22213ae0028a82e5adf3f286dfd4f01e0997dc9f1dcd8e831a0a85e799be17)
## Related Projects
- [DPlayer](https://github.com/DIYgod/DPlayer)
- [Official danmaku data (https://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)

@ -0,0 +1,3 @@
Can be username and IP
username
0.0.0.0

@ -0,0 +1,32 @@
version: '2.1'
services:
mongo:
image: mongo:latest
ports:
- 27017:27017
volumes:
- ~/dplayer/db:/data/db
redis:
image: redis:latest
ports:
- "6379:6379"
web:
build: .
links:
- mongo
- redis
depends_on:
- mongo
- redis
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"
volumes:
- ~/dplayer/logs:/usr/src/app/logs
- ~/dplayer/pm2logs:/root/.pm2/logs

@ -0,0 +1,14 @@
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.listen(1207);

@ -0,0 +1,16 @@
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;

@ -0,0 +1,18 @@
{
"name": "DPlayer_nodejs",
"version": "0.0.1",
"description": "",
"main": "index.js",
"author": "DIYgod",
"license": "MIT",
"devDependencies": {},
"dependencies": {
"blueimp-md5": "^2.4.0",
"express": "^4.13.4",
"log4js": "^0.6.36",
"mongoose": "^4.1.9",
"node-fetch": "^1.6.3",
"redis": "^2.6.2",
"xml2js": "^0.4.17"
}
}

@ -0,0 +1,17 @@
var https = require('https');
module.exports = function (req, res, next) {
https.get(`https://api.prprpr.me/count/?id=DIYgod-DPlayer&action=add`);
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();
}
};

@ -0,0 +1,134 @@
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)
);
}
});
}
};

@ -0,0 +1,43 @@
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(`dplayer${id}`, function(err, reply) {
if (reply) {
logger.info(`DPlayer id ${id} form redis, IP: ${ip}`);
res.send(reply);
}
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);
})
}
});
};

@ -0,0 +1,17 @@
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);
})
};

@ -0,0 +1,118 @@
var fs = require('fs');
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;");
}
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
var blanklist = fs.readFileSync('blacklist').toString().split('\n');
if (blanklist.indexOf(ip.split(',')[0]) !== -1) {
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;
}
// check black username
if (blanklist.indexOf(jsonStr.author) !== -1) {
logger.info(`Reject POST form ${jsonStr.author} for black user.`);
res.send(`{"code": -5, "msg": "Rejected for black user."}`);
return;
}
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);
}
};

@ -0,0 +1,72 @@
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)
);
}
});
}
};

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

@ -0,0 +1,11 @@
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);
module.exports = mongoose;

@ -0,0 +1,26 @@
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
};
Loading…
Cancel
Save