Skip to content

Commit

Permalink
feat(route): add route for pixivision (DIYgod#16741)
Browse files Browse the repository at this point in the history
* feat(route): add route for pixivision

* fix: use got instead of fetch

* chore: update maintainer username

* refactor: Replace custom tweet processing with embedded Twitter iframe
  • Loading branch information
SnowAgar25 authored Sep 17, 2024
1 parent 09387f7 commit 20dfcb9
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 0 deletions.
84 changes: 84 additions & 0 deletions lib/routes/pixivision/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Route, DataItem, Data } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { processContent } from './utils';

export const route: Route = {
path: '/:lang/:category?',
categories: ['anime'],
example: '/pixivision/zh-tw',
parameters: { lang: 'Language', category: 'Category' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
name: 'Category',
maintainers: ['SnowAgar25'],
description: `:::tip
\`https://www.pixivision.net/zh-tw/c/interview\` โ†’ \`/pixivision/zh-tw/interview\`
:::`,
radar: [
{
source: ['www.pixivision.net/:lang'],
target: '/:lang',
},
{
source: ['www.pixivision.net/:lang/c/:category'],
target: '/:lang/:category',
},
],
handler,
};

async function handler(ctx): Promise<Data> {
const { lang, category } = ctx.req.param();
const baseUrl = 'https://www.pixivision.net';
const url = category ? `${baseUrl}/${lang}/c/${category}` : `${baseUrl}/${lang}`;

const headers = {
headers: {
Cookie: `user_lang=${lang.replace('-', '_')}`, // zh-tw โ†’ zh_tw
},
};

const { data: response } = await got(url, headers);
const $ = load(response);

const list = $('li.article-card-container a[data-gtm-action="ClickTitle"]')
.map((_, elem) => ({
title: $(elem).text(),
link: new URL($(elem).attr('href') ?? '', baseUrl).href,
}))
.toArray();

const items = await Promise.all(
list.map(async (item) => {
const result = await cache.tryGet(item.link, async () => {
const { data: articleData } = await got(item.link, headers);
const $article = load(articleData);

const processedDescription = processContent($article, lang);

return {
title: item.title,
description: processedDescription,
link: item.link,
pubDate: parseDate($article('time').attr('datetime') ?? ''),
} as DataItem;
});
return result;
})
);

return {
title: `${$('.ssc__header').length ? $('.ssc__header').text() : 'New'} - pixivision`,
link: url,
item: items.filter((item): item is DataItem => !!item),
};
}
6 changes: 6 additions & 0 deletions lib/routes/pixivision/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Namespace } from '@/types';

export const namespace: Namespace = {
name: 'pixivision',
url: 'www.pixivision.net',
};
86 changes: 86 additions & 0 deletions lib/routes/pixivision/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { CheerioAPI } from 'cheerio';
import { config } from '@/config';

const multiImagePrompt = {
en: (count) => `${count} images in total`,
zh: (count) => `ๅ…ฑ${count}ๅผ ๅ›พ`,
'zh-tw': (count) => `ๅ…ฑ${count}ๅผตๅœ–`,
ko: (count) => `์ด ${count}๊ฐœ์˜ ์ด๋ฏธ์ง€`,
ja: (count) => `่จˆ${count}ๆžšใฎ็”ปๅƒ`,
};

export function processContent($: CheerioAPI, lang: string): string {
// ็งป้™คไฝœ่€…้ ญๅƒ
$('.am__work__user-icon-container').remove();

// ๆ’็•ซๆจ™้กŒ&ไฝœ่€…
$('.am__work__title').attr('style', 'display: inline;');
$('.am__work__user-name').attr('style', 'display: inline; margin-left: 10px;');

// ่™•็†ๅคšๅผตๅœ–็‰‡็š„ๆ็คบ
$('.mic__label').each((_, elem) => {
const $label = $(elem);
const count = $label.text();
const $workContainer = $label.parentsUntil('.am__work').last().parent();
const $titleContainer = $workContainer.find('.am__work__title-container');

$titleContainer.append(`<p style="float: right; margin: 0;">${multiImagePrompt[lang](count)}</p>`);
$label.remove();
});

// ๆ’็•ซ้–“้š”
$('.article-item, ._feature-article-body__pixiv_illust').after('<br>');

// Remove Label & Tags
$('.arc__thumbnail-label').remove();
$('.arc__footer-container').remove();

// pixivision card
$('article._article-card').each((_, article) => {
const $article = $(article);

const $thumbnail = $article.find('._thumbnail');
const thumbnailStyle = $thumbnail.attr('style');
const bgImageMatch = thumbnailStyle?.match(/url\((.*?)\)/);
const imageUrl = bgImageMatch ? bgImageMatch[1] : '';

$thumbnail.remove();

if (imageUrl) {
$article.prepend(`<img src="${imageUrl}" alt="Article thumbnail">`);
}
});

// ่™•็† tweet
$('.fab__script').each((_, elem) => {
const $elem = $(elem);
const $link = $elem.find('blockquote > a');
const href = $link.attr('href');

if (href) {
const match = href.match(/\/status\/(\d+)/);
if (match) {
const tweetId = match[1];
$elem.html(`
<iframe
scrolling="no"
frameborder="0"
allowtransparency="true"
allowfullscreen="true"
class=""
style="position: static; visibility: visible; display: block; width: 550px; height: 1000px; flex-grow: 1;"
title="X Post"
src="https://platform.twitter.com/embed/Tweet.html?id=${tweetId}"
></iframe>
`);
$elem.find('blockquote').remove();
}
}
});

return (
$('.am__body')
.html()
?.replace(/https:\/\/i\.pximg\.net/g, config.pixiv.imgProxy || '') || ''
);
}

0 comments on commit 20dfcb9

Please sign in to comment.