parent
e796e2f712
commit
b3c5ed21ec
30 changed files with 1295 additions and 1910 deletions
@ -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 |
||||
} |
||||
} |
@ -1,2 +1,3 @@ |
||||
node_modules |
||||
logs/DPlayer.log* |
||||
error.log |
||||
combined.log |
@ -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, |
||||
} |
||||
}; |
@ -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; |
@ -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, '&') |
||||
.replace(/</g, '<') |
||||
.replace(/>/g, '>') |
||||
.replace(/"/g, '"') |
||||
.replace(/'/g, ''') |
||||
.replace(/\//g, '/'); |
||||
} |
||||
|
||||
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, "&") |
||||
.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
|
||||
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, "&") |
||||
.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
|
||||
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); |
||||
}, |
||||
}; |
Loading…
Reference in new issue