@uwu-codes/furaffinity-api
Version:
FurAffinity wrapper for Node.js
393 lines (392 loc) • 17.5 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParseMyWatchingList = exports.ParseWatchingList = exports.ParseAuthor = exports.ParseSubmission = exports.ParseSubmissionsPaging = exports.ParseBrowsePaging = exports.ParseSearchPaging = exports.ParseScrapsPaging = exports.ParseGalleryPaging = exports.ParseFigures = exports.ParseFigure = void 0;
const cheerio_1 = __importDefault(require("cheerio"));
const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
const Enums_1 = require("./Enums");
const _1 = require(".");
const Request_1 = require("./Request");
/**
* Convert author name to author id
* @param name author name
*/
function convertNameToId(name) {
return name.trim().replace('_', '').toLowerCase();
}
/**
* Get class names from element
* @param element CheerioElement
*/
function classNames(element) {
return element.attribs.class.split(' ');
}
function checkSystemMessage($) {
// Check system message
const noticeMessage = $('section.notice-message');
if (noticeMessage.length !== 0) {
const systemMessage = noticeMessage[0].childNodes[1].childNodes[3].childNodes[0].nodeValue;
throw new Error(systemMessage);
}
}
/**
* Parse result from figure element
* @param figure CheerioElement
*/
function ParseFigure(figure, selector) {
var _a, _b, _c, _d;
const id = (_a = figure.attribs.id.split('-').pop()) !== null && _a !== void 0 ? _a : '';
const thumb = 'http:' + figure.childNodes[0].childNodes[0].childNodes[0].childNodes[0].attribs.src;
const authorName = selector.find('figcaption p:last-child a').first().attr().title;
const authorId = convertNameToId(authorName);
return {
type: Enums_1.SubmissionType[classNames(figure)[1].split('-').pop()],
id,
title: (_c = (_b = figure.childNodes[1].childNodes[0].childNodes[0].childNodes[0]) === null || _b === void 0 ? void 0 : _b.nodeValue) !== null && _c !== void 0 ? _c : '',
url: `${Request_1.ENDPOINT}/view/${id}`,
rating: Enums_1.Rating[(_d = classNames(figure)[0]
.split('-')
.pop()) === null || _d === void 0 ? void 0 : _d.replace(/^[a-z]/, ($1) => $1.toUpperCase())],
thumb: {
icon: thumb.replace(/@\d+?-/g, '@75-'),
tiny: thumb.replace(/@\d+?-/g, '@150-'),
small: thumb.replace(/@\d+?-/g, '@300-'),
medium: thumb.replace(/@\d+?-/g, '@800-'),
large: thumb.replace(/@\d+?-/g, '@1600-'),
},
author: {
id: authorId,
url: `${Request_1.ENDPOINT}/user/${authorId}`,
name: authorName,
},
getSubmission: () => __awaiter(this, void 0, void 0, function* () {
return yield _1.Submission(id);
}),
};
}
exports.ParseFigure = ParseFigure;
/**
* Parse all figure's info from HTML
* @param body HTML document
*/
function ParseFigures(body) {
const $ = cheerio_1.default.load(body);
const results = [];
$('figure').each((index, figure) => {
results.push(ParseFigure(figure, $(figure)));
});
return results;
}
exports.ParseFigures = ParseFigures;
function ParseGalleryPaging(body, results, perpage) {
const $ = cheerio_1.default.load(body);
const links = $('.submission-list .aligncenter:nth-child(1) div');
if ($(links[0]).find('form').length > 0) {
results.prevLink = Request_1.ENDPOINT + $(links[0]).find('form').attr()['action'];
const matchs = results.prevLink.match(/\/gallery\/(.+?)\/(\d+?)\/$/);
if (matchs) {
const id = matchs[1];
const page = Number.parseInt(matchs[2]);
results.prev = () => _1.Gallery(id, page, perpage);
}
}
if ($(links[2]).find('form').length > 0) {
results.nextLink = Request_1.ENDPOINT + $(links[2]).find('form').attr()['action'];
const matchs = results.nextLink.match(/\/gallery\/(.+?)\/(\d+?)\/$/);
if (matchs) {
const id = matchs[1];
const page = Number.parseInt(matchs[2]);
results.next = () => _1.Gallery(id, page, perpage);
}
}
return results;
}
exports.ParseGalleryPaging = ParseGalleryPaging;
function ParseScrapsPaging(body, results, perpage) {
const $ = cheerio_1.default.load(body);
const links = $('.submission-list .aligncenter:nth-child(1) div');
if ($(links[0]).find('form').length > 0) {
results.prevLink = Request_1.ENDPOINT + $(links[0]).find('form').attr()['action'];
const matchs = results.prevLink.match(/\/scraps\/(.+?)\/(\d+?)\/$/);
if (matchs) {
const id = matchs[1];
const page = Number.parseInt(matchs[2]);
results.prev = () => _1.Scraps(id, page, perpage);
}
}
if ($(links[2]).find('form').length > 0) {
results.nextLink = Request_1.ENDPOINT + $(links[2]).find('form').attr()['action'];
const matchs = results.nextLink.match(/\/scraps\/(.+?)\/(\d+?)\/$/);
if (matchs) {
const id = matchs[1];
const page = Number.parseInt(matchs[2]);
results.next = () => _1.Scraps(id, page, perpage);
}
}
return results;
}
exports.ParseScrapsPaging = ParseScrapsPaging;
function ParseSearchPaging(body, results, query, options) {
const $ = cheerio_1.default.load(body);
const links = $('#search-results .pagination button');
if (!classNames(links[0]).includes('disabled')) {
const newOptions = cloneDeep_1.default(options || {});
newOptions.page = newOptions.page ? newOptions.page - 1 : 1;
newOptions.prev = true;
results.prev = () => _1.Search(query, newOptions);
}
if (!classNames(links[1]).includes('disabled')) {
const newOptions = cloneDeep_1.default(options || {});
newOptions.page = newOptions.page ? newOptions.page + 1 : 2;
newOptions.prev = false;
results.next = () => _1.Search(query, newOptions);
}
return results;
}
exports.ParseSearchPaging = ParseSearchPaging;
function ParseBrowsePaging(body, results, options) {
const $ = cheerio_1.default.load(body);
const links = $('.section-body .navigation:nth-child(1) div');
if ($(links[0]).find('form').length > 0) {
results.prevLink = Request_1.ENDPOINT + $(links[0]).find('form').attr()['action'];
const matchs = results.prevLink.match(/\/browse\/(\d+?)$/);
if (matchs) {
const page = Number.parseInt(matchs[1]);
options = options !== null && options !== void 0 ? options : {};
options.page = page;
results.prev = () => _1.Browse(options);
}
}
if ($(links[2]).find('form').length > 0) {
results.nextLink = Request_1.ENDPOINT + $(links[2]).find('form').attr()['action'];
const matchs = results.nextLink.match(/\/browse\/(\d+?)$/);
if (matchs) {
const page = Number.parseInt(matchs[1]);
options = options !== null && options !== void 0 ? options : {};
options.page = page;
results.next = () => _1.Browse(options);
}
}
return results;
}
exports.ParseBrowsePaging = ParseBrowsePaging;
function ParseSubmissionsPaging(body, results) {
const $ = cheerio_1.default.load(body);
const links = $('.section-body .aligncenter:nth-child(1)');
if ($(links).find('a.more-half').length === 2) {
results.prevLink = Request_1.ENDPOINT + $(links).find('a.more-half').first().attr()['href'];
results.nextLink = Request_1.ENDPOINT + $(links).find('a.more-half').last().attr()['href'];
}
else if ($(links).find('a.more.prev').length === 1) {
results.prevLink = Request_1.ENDPOINT + $(links).find('a.more.prev').first().attr()['href'];
}
else if ($(links).find('a.more').length === 1) {
results.nextLink = Request_1.ENDPOINT + $(links).find('a.more').first().attr()['href'];
}
if (results.prevLink) {
const matchs = results.prevLink.match(/\/submissions\/(.+?)~(.+?)@(\d+)\/$/);
if (matchs) {
const sort = matchs[1];
const startAt = matchs[2];
const perpage = matchs[3];
const options = {
sort: sort,
startAt: startAt,
perpage: perpage,
};
results.prev = () => _1.Submissions(options);
}
}
if (results.nextLink) {
const matchs = results.nextLink.match(/\/submissions\/(.+?)~(.+?)@(\d+)\/$/);
if (matchs) {
const sort = matchs[1];
const startAt = matchs[2];
const perpage = matchs[3];
const options = {
sort: sort,
startAt: startAt,
perpage: perpage,
};
results.next = () => _1.Submissions(options);
}
}
return results;
}
exports.ParseSubmissionsPaging = ParseSubmissionsPaging;
/**
* Get submission's info
* @param body HTML document
* @param id Subumission id
*/
function ParseSubmission(body, id) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
const $ = cheerio_1.default.load(body);
checkSystemMessage($);
// Get main nodes
const main = $('#columnpage');
const sidebar = main.find('.submission-sidebar');
const content = main.find('.submission-content');
const stats = sidebar.find('.stats-container');
const info = sidebar.find('.info');
const tags = sidebar.find('.tags-row .tags a');
// buttons
let downloadUrl = `http:${sidebar.find('.buttons .download a')[0].attribs.href}`;
const favLink = `http://furaffinity.net${sidebar.find('.buttons .fav a')[0].attribs.href}`;
// header
const title = (_b = (_a = content
.find('.submission-id-sub-container .submission-title p')[0]
.childNodes[0].data) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
const authorName = (_d = (_c = content.find('.submission-id-sub-container a strong')[0].childNodes[0].data) === null || _c === void 0 ? void 0 : _c.trim()) !== null && _d !== void 0 ? _d : '';
const authorId = convertNameToId(authorName);
const posted = content.find('.submission-id-sub-container strong span')[0].attribs.title;
const authorAvatar = `http:${content.find('.submission-id-avatar img')[0].attribs.src}`;
const authorShinies = !!$('.shinies-promo');
// stats
const rating = Enums_1.Rating[(_e = stats.find('.rating span')[0].childNodes[0].data) === null || _e === void 0 ? void 0 : _e.trim()];
const favorites = Number.parseInt((_g = (_f = stats.find('.favorites span')[0].childNodes[0].data) === null || _f === void 0 ? void 0 : _f.trim()) !== null && _g !== void 0 ? _g : '');
const comments = Number.parseInt((_j = (_h = stats.find('.comments span')[0].childNodes[0].data) === null || _h === void 0 ? void 0 : _h.trim()) !== null && _j !== void 0 ? _j : '');
const views = Number.parseInt((_l = (_k = stats.find('.views span')[0].childNodes[0].data) === null || _k === void 0 ? void 0 : _k.trim()) !== null && _l !== void 0 ? _l : '');
// info
const category = Enums_1.Category[(_m = info.find('.category-name')[0].childNodes[0].data) === null || _m === void 0 ? void 0 : _m.trim()];
const species = Enums_1.Species[(_o = info[0].childNodes[3].childNodes[2].childNodes[0].data) === null || _o === void 0 ? void 0 : _o.trim()];
const gender = Enums_1.Gender[(_p = info[0].childNodes[5].childNodes[2].childNodes[0].data) === null || _p === void 0 ? void 0 : _p.trim()];
// fix url when category is story or poetry
if (category === Enums_1.Category.Story || category === Enums_1.Category.Poetry) {
downloadUrl = downloadUrl.replace('d.facdn.net/download/', 'd.facdn.net/');
}
const previewUrl = content.find('.submission-area img').length > 0
? `http:${content.find('.submission-area img')[0].attribs['data-preview-src']}`
: undefined;
return {
id,
url: `http://www.furaffinity.net/view/${id}`,
title: title,
posted: Date.parse(posted),
favLink,
rating: rating,
author: {
id: authorId,
name: authorName,
url: `http://www.furaffinity.net/user/${authorId}`,
avatar: authorAvatar,
shinies: authorShinies,
},
content: {
category,
species,
gender,
},
stats: {
favorites,
comments,
views,
},
downloadUrl,
previewUrl,
keywords: tags
.map((index, tag) => {
var _a, _b;
return (_b = (_a = tag.childNodes[0].data) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
})
.get(),
fave: () => __awaiter(this, void 0, void 0, function* () {
yield Request_1.FaveSubmission(favLink);
}),
};
}
exports.ParseSubmission = ParseSubmission;
/**
* Get author's info
* @param body HTML document
*/
function ParseAuthor(body) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
const $ = cheerio_1.default.load(body);
checkSystemMessage($);
const name = (_b = (_a = $('.userpage-flex-item.username span')[0].childNodes[0].data) === null || _a === void 0 ? void 0 : _a.trim().slice(1)) !== null && _b !== void 0 ? _b : '';
const id = convertNameToId(name);
const url = `http://www.furaffinity.net/user/${id}`;
const shinies = !!$('.userpage-layout-left-col-content > a:nth-child(4)');
const avatar = `https:${$('.user-nav-avatar')[0].attribs.src}`;
const statsCells = $('.userpage-section-right .cell');
const views = (_d = (_c = statsCells[0].childNodes[2].data) === null || _c === void 0 ? void 0 : _c.trim()) !== null && _d !== void 0 ? _d : '0';
const submissions = (_f = (_e = statsCells[0].childNodes[6].data) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : '0';
const favs = (_h = (_g = statsCells[0].childNodes[10].data) === null || _g === void 0 ? void 0 : _g.trim()) !== null && _h !== void 0 ? _h : '0';
const commentsEarned = (_k = (_j = statsCells[1].childNodes[2].data) === null || _j === void 0 ? void 0 : _j.trim()) !== null && _k !== void 0 ? _k : '0';
const commentsMade = (_m = (_l = statsCells[1].childNodes[6].data) === null || _l === void 0 ? void 0 : _l.trim()) !== null && _m !== void 0 ? _m : '0';
const journals = (_p = (_o = statsCells[1].childNodes[10].data) === null || _o === void 0 ? void 0 : _o.trim()) !== null && _p !== void 0 ? _p : '0';
return {
id,
name,
url,
avatar,
shinies,
stats: {
views: Number.parseInt(views),
submissions: Number.parseInt(submissions),
favs: Number.parseInt(favs),
commentsEarned: Number.parseInt(commentsEarned),
commentsMade: Number.parseInt(commentsMade),
journals: Number.parseInt(journals),
},
};
}
exports.ParseAuthor = ParseAuthor;
/**
* Get all Author's info from peer page of watching list
* @param body HTML document
*/
function ParseWatchingList(body) {
const $ = cheerio_1.default.load(body);
checkSystemMessage($);
return $('.watch-list-items a')
.map((index, a) => {
var _a, _b;
const name = (_b = (_a = a.childNodes[0].data) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
const id = convertNameToId(name);
const url = `${Request_1.ENDPOINT}/user/${id}`;
return {
id,
name,
url,
};
})
.get();
}
exports.ParseWatchingList = ParseWatchingList;
/**
* Get all Author's info from peer page of watching list
* @param body HTML document
*/
function ParseMyWatchingList(body) {
const $ = cheerio_1.default.load(body);
checkSystemMessage($);
return $('.flex-item-watchlist')
.map((index, div) => {
var _a, _b;
const avatar = `https:${$(div).find('img.avatar')[0].attribs.src}`;
const name = (_b = (_a = $(div).find('.flex-item-watchlist-controls a strong')[0].childNodes[0].data) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
const id = convertNameToId(name);
const url = `${Request_1.ENDPOINT}/user/${id}`;
return {
id,
name,
url,
avatar,
};
})
.get();
}
exports.ParseMyWatchingList = ParseMyWatchingList;