pixiv-illust
Version:
A third-party Node.js library for Pixiv.
211 lines (189 loc) • 6.12 kB
JavaScript
import 'babel-polyfill';
import login from './login';
import { loginRequired } from './login';
import cheerio from 'cheerio';
import Promise from 'Bluebird';
import _ from 'lodash';
import path from 'path';
import { cachedProperty,replacePlaceholder } from './utility';
import extend from 'extend';
const request = Promise.promisifyAll(require('request'));
const fs = Promise.promisifyAll(require('fs'));
const PAGE = 'http://www.pixiv.net/member_illust.php';
const TYPE = {
NORMAL: 0,
UGOIRA: 1,
MULTIPLE: 2
};
function validType(...types) {
const VALID_TYPES = Object.values(TYPE);
types.forEach(type => {
if (!VALID_TYPES.includes(type)) {
throw new Error('Unvalid types.');
}
});
return (target, prop, descriptor) => {
const method = descriptor.value;
descriptor.value = async function(...args){
let type = await this._type();
if (!types.includes(type)){
throw new Error('Not allowed in this type of illust.');
}
return method.call(this,args);
};
return descriptor;
};
}
class Illust {
constructor(id){
this.id = id;
}
getPageUrl(mode='medium'){
return `${PAGE}?mode=${mode}&illust_id=${this.id}`;
}
get url(){
return this.getPageUrl();
}
async getContent(){
let response = await request.getAsync({
url: this.url,
headers: {
'Host': 'www.pixiv.net',
'Referer': 'http://www.pixiv.net/',
},
jar: login.cookieJar
});
return cheerio.load(response.body);
}
async getInfo(){
let $ = await this.getContent();
let $workInfo = $('.work-info');
let $metas = $workInfo.find('.meta li');
return {
id: this.id,
title: $workInfo.find('.title').text(),
author: $('.user').text().trim(),
tools: $workInfo.find('.tools li').map((i,elem) => {
return $(elem).text().trim();
}).get(),
page: this.url,
date: $metas.eq(0).text()
};
}
async _type(){
let $ = await this.getContent();
if ($('.works_display .multiple').length) {
return TYPE.MULTIPLE;
} else if ($('._ugoku-illust-player-container').length) {
return TYPE.UGOIRA;
} else {
return TYPE.NORMAL;
}
}
(TYPE.NORMAL)
async _getImageUrl(){
let $ = await this.getContent();
return $('.original-image').attr('data-src');
}
(TYPE.MULTIPLE)
async _getNum(){
let $ = await this.getContent();
let text= $('.meta li').eq(1).text();
return +(text.match(/(\d+)P/) || [])[1] || 0;
}
(TYPE.MULTIPLE)
async _getImageUrls(){
let num = await this._getNum();
return Promise.all(_.range(num).map(index =>
request.getAsync({
url: `${this.getPageUrl('manga_big')}&page=${index}`,
jar: login.cookieJar
})
)).then(responses => {
return responses.map(response => {
let $ = cheerio.load(response.body);
return $('img').attr('src');
});
});
}
(TYPE.MULTIPLE)
async _guessImageUrls(){
let num = await this._getNum();
let response = await request.getAsync({
url: `${this.getPageUrl('manga_big')}&page=0`,
jar: login.cookieJar
});
let $ = cheerio.load(response.body);
let img0 = $('img').attr('src');
return _.range(num).map(index => img0.replace(/_p(\d+)\..*?$/,($,$1) => {
return $.replace(`_p${$1}`,`_p${index}`);
}));
}
(TYPE.UGOIRA)
async _getPackUrl(){
let $ = await this.getContent();
let $script = $('script').filter((i,elem) => {
return $(elem).text().includes('pixiv.context.ugokuIllustFullscreenData');
});
let pixiv = (new Function('var pixiv={context:{}};' + $script.text() +';return pixiv;'))();
return pixiv.context.ugokuIllustFullscreenData.src;
}
async _getDownloadQueue(){
let type = await this._type();
let info = await this.getInfo();
if (type === TYPE.MULTIPLE) {
let urls = this._guessImageUrls();
return urls.map((url,index) => {
return extend({},info,{
title: `${info.title} - ${_.padStart(index+1, 2, '0')}`,
url: url,
suffix: path.extname(url)
});
});
}
let url;
if (type === TYPE.NORMAL) {
url = await this._getImageUrl();
}
if (type === TYPE.UGOIRA) {
url = await this._getPackUrl();
}
extend(info,{
url: url,
suffix: path.extname(url)
});
return [info];
}
async download(filepath='{{author}} - {{title}}{{suffix}}'){
let infos = await this._getDownloadQueue();
for (let info of infos) {
try {
let file = replacePlaceholder(filepath,info);
console.log(`Downloading ${path.basename(file)} ...`);
let resp = await request.getAsync({
url: info.url,
headers: {
'Referer': this.url
},
// do not encode the response data
// directly dumps to the files.
encoding: null,
jar: login.cookieJar
});
await fs.writeFileAsync(file,resp.body);
} catch(e) {
// do nothing
}
}
}
}
export default Illust;