commit
ce24a65ca1
18 changed files with 591 additions and 0 deletions
@ -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,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, "&") |
||||
.replace(/</g, "<") |
||||
.replace(/>/g, ">") |
||||
.replace(/"/g, """) |
||||
.replace(/'/g, "'") |
||||
.replace(/\//g, "/"); |
||||
} |
||||
|
||||
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…
Reference in new issue