forked from flanchan/doushio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
5.4 KiB
228 lines
5.4 KiB
5 years ago
|
var async = require('async'),
|
||
|
config = require('./config'),
|
||
|
child_process = require('child_process'),
|
||
|
db = require('./db'),
|
||
|
etc = require('../etc'),
|
||
|
fs = require('fs'),
|
||
|
hooks = require('../hooks'),
|
||
|
path = require('path'),
|
||
|
winston = require('winston');
|
||
|
|
||
|
exports.Onegai = db.Onegai;
|
||
|
exports.config = config;
|
||
|
|
||
|
var image_attrs = ('src thumb dims size MD5 hash imgnm spoiler realthumb vint'
|
||
|
+ ' apng mid audio video duration').split(' ');
|
||
|
exports.image_attrs = image_attrs;
|
||
|
|
||
|
exports.send_dead_image = function (kind, filename, resp) {
|
||
|
filename = dead_path(kind, filename);
|
||
|
var stream = fs.createReadStream(filename);
|
||
|
stream.once('error', function (err) {
|
||
|
if (err.code == 'ENOENT') {
|
||
|
resp.writeHead(404);
|
||
|
resp.end('Image not found');
|
||
|
}
|
||
|
else {
|
||
|
winston.error(err);
|
||
|
resp.end();
|
||
|
}
|
||
|
});
|
||
|
stream.once('open', function () {
|
||
|
var h = {
|
||
|
'Cache-Control': 'no-cache, no-store',
|
||
|
'Expires': 'Thu, 01 Jan 1970 00:00:00 GMT',
|
||
|
};
|
||
|
try {
|
||
|
h['Content-Type'] = require('mime').lookup(filename);
|
||
|
} catch (e) {}
|
||
|
resp.writeHead(200, h);
|
||
|
stream.pipe(resp);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
hooks.hook_sync('extractPost', function (post) {
|
||
|
if (!is_image(post))
|
||
|
return;
|
||
|
var image = {};
|
||
|
image_attrs.forEach(function (key) {
|
||
|
if (key in post) {
|
||
|
image[key] = post[key];
|
||
|
delete post[key];
|
||
|
}
|
||
|
});
|
||
|
if (image.dims.split)
|
||
|
image.dims = image.dims.split(',').map(parse_number);
|
||
|
image.size = parse_number(image.size);
|
||
|
delete image.hash;
|
||
|
post.image = image;
|
||
|
});
|
||
|
|
||
|
function parse_number(n) {
|
||
|
return parseInt(n, 10);
|
||
|
}
|
||
|
|
||
|
hooks.hook_sync('inlinePost', function (info) {
|
||
|
var post = info.dest, image = info.src.image;
|
||
|
if (!image)
|
||
|
return;
|
||
|
image_attrs.forEach(function (key) {
|
||
|
if (key in image)
|
||
|
post[key] = image[key];
|
||
|
});
|
||
|
});
|
||
|
|
||
|
function publish(alloc, cb) {
|
||
|
var mvs = [];
|
||
|
var haveComp = alloc.tmps.comp && alloc.image.realthumb;
|
||
|
for (var kind in alloc.tmps) {
|
||
|
var src = media_path('tmp', alloc.tmps[kind]);
|
||
|
|
||
|
// both comp and thumb go in thumb/
|
||
|
var destDir = (kind == 'comp') ? 'thumb' : kind;
|
||
|
// hack for stupid thumb/realthumb business
|
||
|
var destKey = kind;
|
||
|
if (haveComp) {
|
||
|
if (kind == 'thumb')
|
||
|
destKey = 'realthumb';
|
||
|
else if (kind == 'comp')
|
||
|
destKey = 'thumb';
|
||
|
}
|
||
|
var dest = media_path(destDir, alloc.image[destKey]);
|
||
|
|
||
|
mvs.push(etc.cpx.bind(etc, src, dest));
|
||
|
}
|
||
|
async.parallel(mvs, cb);
|
||
|
}
|
||
|
|
||
|
function validate_alloc(alloc) {
|
||
|
if (!alloc || !alloc.image || !alloc.tmps)
|
||
|
return;
|
||
|
for (var dir in alloc.tmps) {
|
||
|
var fnm = alloc.tmps[dir];
|
||
|
if (!/^[\w_]+$/.test(fnm)) {
|
||
|
winston.warn("Suspicious filename: "
|
||
|
+ JSON.stringify(fnm));
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
hooks.hook("buryImage", function (info, callback) {
|
||
|
if (!info.src)
|
||
|
return callback(null);
|
||
|
/* Just in case */
|
||
|
var m = /^\d+\w*\.\w+$/;
|
||
|
if (!info.src.match(m))
|
||
|
return callback(etc.Muggle('Invalid image.'));
|
||
|
var mvs = [mv.bind(null, 'src', info.src)];
|
||
|
function try_thumb(path, t) {
|
||
|
if (!t)
|
||
|
return;
|
||
|
if (!t.match(m))
|
||
|
return callback(etc.Muggle('Invalid thumbnail.'));
|
||
|
mvs.push(mv.bind(null, path, t));
|
||
|
}
|
||
|
try_thumb('thumb', info.thumb);
|
||
|
try_thumb('thumb', info.realthumb);
|
||
|
try_thumb('mid', info.mid);
|
||
|
async.parallel(mvs, callback);
|
||
|
function mv(p, nm, cb) {
|
||
|
etc.movex(media_path(p, nm), dead_path(p, nm), cb);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
function is_image(image) {
|
||
|
return image && (image.src || image.vint);
|
||
|
}
|
||
|
|
||
|
function media_path(dir, filename) {
|
||
|
return path.join(config.MEDIA_DIRS[dir], filename);
|
||
|
}
|
||
|
exports.media_path = media_path;
|
||
|
|
||
|
function dead_path(dir, filename) {
|
||
|
return path.join(config.MEDIA_DIRS.dead, dir, filename);
|
||
|
}
|
||
|
|
||
|
function make_dir(base, key, cb) {
|
||
|
var dir = base ? path.join(base, key) : config.MEDIA_DIRS[key];
|
||
|
etc.checked_mkdir(dir, cb);
|
||
|
}
|
||
|
exports._make_media_dir = make_dir;
|
||
|
|
||
|
exports.make_media_dirs = function (cb) {
|
||
|
var keys = ['src', 'thumb', 'vint', 'dead'];
|
||
|
if (!is_standalone())
|
||
|
keys.push('tmp');
|
||
|
if (config.EXTRA_MID_THUMBNAILS)
|
||
|
keys.push('mid');
|
||
|
async.forEach(keys, make_dir.bind(null, null), function (err) {
|
||
|
if (err)
|
||
|
return cb(err);
|
||
|
var dead = config.MEDIA_DIRS.dead;
|
||
|
var keys = ['src', 'thumb'];
|
||
|
if (config.EXTRA_MID_THUMBNAILS)
|
||
|
keys.push('mid');
|
||
|
async.forEach(keys, make_dir.bind(null, dead), cb);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
exports.serve_image = function (req, resp) {
|
||
|
var m = /^\/(src|thumb|mid|vint)(\/\d+\.\w+)$/.exec(req.url);
|
||
|
if (!m)
|
||
|
return false;
|
||
|
var root = config.MEDIA_DIRS[m[1]];
|
||
|
if (!root)
|
||
|
return false;
|
||
|
require('send')(req, m[2], {root: root}).pipe(resp);
|
||
|
return true;
|
||
|
};
|
||
|
|
||
|
exports.squish_MD5 = function (hash) {
|
||
|
if (typeof hash == 'string')
|
||
|
hash = new Buffer(hash, 'hex');
|
||
|
return hash.toString('base64').replace(/\//g, '_').replace(/=*$/, '');
|
||
|
};
|
||
|
|
||
|
exports.obtain_image_alloc = function (id, cb) {
|
||
|
var onegai = new db.Onegai;
|
||
|
onegai.obtain_image_alloc(id, function (err, alloc) {
|
||
|
onegai.disconnect();
|
||
|
if (err)
|
||
|
return cb(err);
|
||
|
|
||
|
if (validate_alloc(alloc))
|
||
|
cb(null, alloc);
|
||
|
else
|
||
|
cb("Invalid image alloc");
|
||
|
});
|
||
|
};
|
||
|
|
||
|
exports.commit_image_alloc = function (alloc, cb) {
|
||
|
publish(alloc, function (err) {
|
||
|
if (err)
|
||
|
return cb(err);
|
||
|
|
||
|
var o = new db.Onegai;
|
||
|
o.commit_image_alloc(alloc, function (err) {
|
||
|
o.disconnect();
|
||
|
cb(err);
|
||
|
});
|
||
|
});
|
||
|
};
|
||
|
|
||
|
exports.note_hash = function (hash, num) {
|
||
|
if (!config.DUPLICATE_COOLDOWN)
|
||
|
return;
|
||
|
var key = 'hash:' + hash;
|
||
|
db.connect().setex(key, config.DUPLICATE_COOLDOWN, num, function (err) {
|
||
|
if (err)
|
||
|
winston.warn("note hash: " + err);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
var is_standalone = exports.is_standalone = db.is_standalone;
|