feat(route): enhance feed of freecomputerbooks.com with full-text support (#12414)
This commit is contained in:
parent
96b7af5cf4
commit
e3f3ce8b22
|
|
@ -1,16 +1,20 @@
|
|||
const cheerio = require('cheerio');
|
||||
const path = require('path');
|
||||
|
||||
const got = require('@/utils/got');
|
||||
const { art } = require('@/utils/render');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
|
||||
const baseURL = 'https://freecomputerbooks.com/';
|
||||
|
||||
async function cheerioLoad(url) {
|
||||
return cheerio.load((await got(url)).data);
|
||||
}
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const categoryId = ctx.params.category?.trim();
|
||||
const requestURL = categoryId ? new URL(`${categoryId}.html`, baseURL).href : baseURL;
|
||||
|
||||
const response = await got({ method: 'get', url: requestURL });
|
||||
const $ = cheerio.load(response.data);
|
||||
const $ = await cheerioLoad(requestURL);
|
||||
|
||||
// As observation has shown that each page only has one element of the
|
||||
// class, thus to simplify the processing the text is directly extracted.
|
||||
|
|
@ -24,23 +28,22 @@ module.exports = async (ctx) => {
|
|||
|
||||
// For a "Selected New Books" page, the <ul> element's id. is
|
||||
// `newBooksG`; for an ordinary category page, it's `newBooksL`.
|
||||
item: $('ul[id^=newBooks] > li')
|
||||
.toArray()
|
||||
.map((elem) => buildPostItem($(elem), categoryTitle, $)),
|
||||
item: await Promise.all(
|
||||
$('ul[id^=newBooks] > li')
|
||||
.toArray()
|
||||
.map((elem) => buildPostItem($(elem), categoryTitle, ctx.cache))
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
function buildPostItem(listItem, categoryTitle, $) {
|
||||
// Cover in original size, e.g., `covers/xyz_43x55.jpg` -> `covers/xyz.jpg`
|
||||
const image = listItem.find('img');
|
||||
image.attr('src', image.attr('src').replace(/_\d+x\d+/, ''));
|
||||
function buildPostItem(listItem, categoryTitle, cache) {
|
||||
const $ = cheerio.load(''); // the only use below doesn't care about the content
|
||||
|
||||
const postLink = listItem.find('a:first');
|
||||
const postInfo = listItem.find('p:contains("Post under")');
|
||||
const postItem = {
|
||||
title: postLink.text(),
|
||||
link: postLink.attr('href'),
|
||||
description: listItem.html(),
|
||||
link: new URL(postLink.attr('href'), baseURL).href,
|
||||
|
||||
// Only a "Selected New Books" page has exclicit categorization info.
|
||||
// for posts; an ordinary category page hasn't, then in which case the
|
||||
|
|
@ -62,5 +65,30 @@ function buildPostItem(listItem, categoryTitle, $) {
|
|||
postItem.pubDate = parseDate(pubDateText);
|
||||
}
|
||||
|
||||
return postItem;
|
||||
return cache.tryGet(postItem.link, () => insertDescriptionInto(postItem));
|
||||
}
|
||||
|
||||
async function insertDescriptionInto(item) {
|
||||
const $ = await cheerioLoad(item.link);
|
||||
|
||||
// Eliminate all comment nodes to avoid their being selected and rendered in
|
||||
// the final output (I know this is actually unnecessary, but please forgive
|
||||
// my mysophobia).
|
||||
$.root()
|
||||
.find('*')
|
||||
.contents()
|
||||
.filter((_, node) => node.type === 'comment')
|
||||
.remove();
|
||||
|
||||
const imageURL = $('#bookdesc img[title]').attr('src');
|
||||
const metadata = $('#booktitle ul').removeAttr('style');
|
||||
const content = $('#bookdesccontent').removeAttr('id');
|
||||
|
||||
metadata.find('li:contains(Share This)').remove();
|
||||
content.find('img[src$="/hot.gif"]').remove();
|
||||
content.find(':contains(Similar Books)').nextAll().addBack().remove();
|
||||
|
||||
item.description = art(path.join(__dirname, 'templates/desc.art'), { imageURL, metadata, content });
|
||||
|
||||
return item;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<figure><img src="{{ imageURL }}"></figure>
|
||||
{{@ metadata }}
|
||||
{{@ content }}
|
||||
Loading…
Reference in New Issue