fix(routes/miyuki): format news titles with bracketed categories and keep full-text body-only (#21377)
* fix(routes/miyuki): format news titles with bracketed categories and keep full-text body-only * fix(routes/miyuki): clean news body extraction and normalize images - Updated lib/routes/miyuki/news.ts to keep item titles as plain article titles and store category information only in the `category` field. - Switched the route to use the canonical Miyuki domain and added the route `url` metadata. - Refined full-text extraction so `description` keeps only the main article body and excludes page header metadata such as section title, article title, date, and category. - Fixed malformed article-body handling for pages like `10217`, where the source HTML structure breaks the original body selector. - Normalized article images by deduplicating `for_pc` / `for_sp` duplicates, converting relative image URLs to absolute URLs, and replacing list-based photo blocks with plain containers so RSS readers do not render black bullets. - Resolved the related CodeFactor/lint issues and verified the file with `oxlint` and `eslint`. * feat(apple): add full-text Apple Newsroom route Add /apple/newsroom for the official Apple Newsroom China feed. Fetch article pages to provide full-text content with cached detail requests, and normalize article metadata and media URLs. Remove gallery navigation, download buttons, video playback controls, empty wrapper nodes, and other non-content UI elements from articles. Format image and video captions as distinct descriptions, and drop duplicate captions when their text matches the article body. * fix(routes/miyuki): simplify image handling in news descriptions * Remove unrelated changes from PR
This commit is contained in:
parent
75cb4a3fb9
commit
df6572b659
|
|
@ -6,13 +6,21 @@ import ofetch from '@/utils/ofetch';
|
|||
import { parseDate } from '@/utils/parse-date';
|
||||
import timezone from '@/utils/timezone';
|
||||
|
||||
const ORIGIN = 'https://miyuki.jp';
|
||||
const ORIGIN = 'https://www.miyuki.jp';
|
||||
const NEWS_LINK = `${ORIGIN}/s/y10/news/list`;
|
||||
const DETAIL_HEADER_SELECTOR = [
|
||||
'.pc__news_detail__title',
|
||||
'.pc__news_detail__title__japanese',
|
||||
'.news_detail__date',
|
||||
'.news_detail__title',
|
||||
'.news_detail__ganre',
|
||||
].join(', ');
|
||||
|
||||
export const route: Route = {
|
||||
path: '/news',
|
||||
example: '/miyuki/news',
|
||||
name: 'News',
|
||||
url: 'www.miyuki.jp/s/y10/news/list',
|
||||
categories: ['new-media'],
|
||||
maintainers: ['KarasuShin'],
|
||||
features: {
|
||||
|
|
@ -33,21 +41,18 @@ async function handler() {
|
|||
const items = await Promise.all(
|
||||
$('.list__side_border li')
|
||||
.toArray()
|
||||
.map(async (item) => {
|
||||
.map((item) => {
|
||||
const $item = $(item);
|
||||
const link = `${ORIGIN}${$item.find('a').attr('href')!}`;
|
||||
return await cache.tryGet(link, async () => {
|
||||
return cache.tryGet(link, async () => {
|
||||
const category = $item.find('p span').last().text();
|
||||
const title = $item.find('a').text();
|
||||
return {
|
||||
title: `${category} - ${$item.find('a').text()}`,
|
||||
title,
|
||||
link,
|
||||
pubDate: timezone(parseDate($item.find('p span').first().text()), +9),
|
||||
category: [category],
|
||||
description: await cache.tryGet(link, async () => {
|
||||
const detailHtml = await ofetch(link);
|
||||
const $detail = load(detailHtml);
|
||||
return $detail('.contents_area__inner').html()!;
|
||||
}),
|
||||
description: await getDescription(link),
|
||||
} as DataItem;
|
||||
});
|
||||
})
|
||||
|
|
@ -59,3 +64,52 @@ async function handler() {
|
|||
item: items,
|
||||
};
|
||||
}
|
||||
|
||||
async function getDescription(link: string) {
|
||||
const detailHtml = await ofetch(link);
|
||||
const $ = load(detailHtml);
|
||||
const content = $('.contents_area__inner');
|
||||
content.children(DETAIL_HEADER_SELECTOR).remove();
|
||||
normalizePhotoLists($, content);
|
||||
content.children().each((_, element) => {
|
||||
const child = $(element);
|
||||
if (!hasMeaningfulHtml(child.html())) {
|
||||
child.remove();
|
||||
}
|
||||
});
|
||||
|
||||
return content.html()?.trim() ?? '';
|
||||
}
|
||||
|
||||
function hasMeaningfulHtml(html?: string | null) {
|
||||
return Boolean(html?.replaceAll(/<br\s*\/?>/g, '').replaceAll(' ', '').trim());
|
||||
}
|
||||
|
||||
function normalizePhotoLists($, content) {
|
||||
content.find('.news_detail__photo_list').each((_, element) => {
|
||||
const list = $(element);
|
||||
const items = list
|
||||
.children('li')
|
||||
.toArray()
|
||||
.flatMap((item) => {
|
||||
const photoItem = $(item);
|
||||
photoItem.find('img.for_sp').remove();
|
||||
photoItem.find('img').each((__, image) => {
|
||||
const img = $(image);
|
||||
const src = img.attr('src');
|
||||
if (!src) {
|
||||
img.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
img.attr('src', new URL(src, ORIGIN).href);
|
||||
img.removeAttr('class');
|
||||
});
|
||||
|
||||
const html = photoItem.html();
|
||||
return hasMeaningfulHtml(html) ? [`<div>${html!.trim()}</div>`] : [];
|
||||
});
|
||||
|
||||
list.replaceWith(items.join('<br /><br />'));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue